diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 55be13834d..c443f8e859 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -85,6 +85,7 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
#define isinsect(A) (is_species(A, /datum/species/insect))
#define isxenoperson(A) (is_species(A, /datum/species/xeno))
#define isstartjelly(A) (is_species(A, /datum/species/jelly/roundstartslime))
+#define isspiderperson(A) (is_species(A, /datum/species/spider))
//more carbon mobs
#define ismonkey(A) (istype(A, /mob/living/carbon/monkey))
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 1119ef44ed..34cfcf2498 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -32,6 +32,9 @@
init_sprite_accessory_subtypes(/datum/sprite_accessory/caps, GLOB.caps_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/insect_wings, GLOB.insect_wings_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/insect_fluff, GLOB.insect_fluffs_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/spider_legs, GLOB.spider_legs_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/spider_spinneret, GLOB.spider_spinneret_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/spider_mandibles, GLOB.spider_mandibles_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/deco_wings, GLOB.deco_wings_list)
//CIT CHANGES START HERE, ADDS SNOWFLAKE BODYPARTS AND MORE
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 52e26b67e7..9437d29a3a 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -76,6 +76,12 @@
init_sprite_accessory_subtypes(/datum/sprite_accessory/insect_fluff, GLOB.insect_fluffs_list)
if(!GLOB.insect_markings_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/insect_markings, GLOB.insect_markings_list)
+ if(!GLOB.spider_legs_list.len)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/spider_legs, GLOB.spider_legs_list)
+ if(!GLOB.spider_spinneret_list.len)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/spider_spinneret, GLOB.spider_spinneret_list)
+ if(!GLOB.spider_mandibles_list.len)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/spider_mandibles, GLOB.spider_mandibles_list)
//CIT CHANGES - genitals and such
if(!GLOB.cock_shapes_list.len)
@@ -177,6 +183,9 @@
"insect_wings" = pick(GLOB.insect_wings_list),
"insect_fluff" = "None",
"insect_markings" = pick(GLOB.insect_markings_list),
+ "spider_legs" = pick(GLOB.spider_legs_list),
+ "spider_spinneret" = pick(GLOB.spider_spinneret_list),
+ "spider_mandibles" = pick(GLOB.spider_mandibles_list),
"taur" = "None",
"mam_body_markings" = snowflake_markings_list.len ? pick(snowflake_markings_list) : "None",
"mam_ears" = snowflake_ears_list ? pick(snowflake_ears_list) : "None",
@@ -278,6 +287,13 @@
if(!findname(.))
break
+/proc/random_unique_spider_name(attempts_to_find_unique_name=10)
+ for(var/i in 1 to attempts_to_find_unique_name)
+ . = capitalize(pick(GLOB.spider_first)) + " " + capitalize(pick(GLOB.spider_last))
+
+ if(!findname(.))
+ break
+
#define SKINTONE2HEX(skin_tone) GLOB.skin_tones[skin_tone] || skin_tone
/proc/random_skin_tone()
diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm
index e1848b21a2..4c1ab5e676 100644
--- a/code/__HELPERS/names.dm
+++ b/code/__HELPERS/names.dm
@@ -16,6 +16,9 @@
/proc/moth_name()
return "[pick(GLOB.moth_first)] [pick(GLOB.moth_last)]"
+/proc/spider_name()
+ return "[pick(GLOB.spider_first)] [pick(GLOB.spider_last)]"
+
/proc/church_name()
var/static/church_name
if (church_name)
diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm
index 09d4aadf50..e071976465 100644
--- a/code/_globalvars/lists/flavor_misc.dm
+++ b/code/_globalvars/lists/flavor_misc.dm
@@ -38,6 +38,9 @@ GLOBAL_LIST_EMPTY(r_wings_list)
GLOBAL_LIST_EMPTY(insect_wings_list)
GLOBAL_LIST_EMPTY(insect_fluffs_list)
GLOBAL_LIST_EMPTY(insect_markings_list)
+GLOBAL_LIST_EMPTY(spider_legs_list)
+GLOBAL_LIST_EMPTY(spider_spinneret_list)
+GLOBAL_LIST_EMPTY(spider_mandibles_list)
GLOBAL_LIST_EMPTY(caps_list)
//a way to index the right bodypart list given the type of bodypart
@@ -60,6 +63,9 @@ GLOBAL_LIST_INIT(mutant_reference_list, list(
"insect_wings" = GLOB.insect_wings_list,
"insect_fluff" = GLOB.insect_fluffs_list,
"insect_markings" = GLOB.insect_markings_list,
+ "spider_legs" = GLOB.spider_legs_list,
+ "spider spinneret" = GLOB.spider_spinneret_list,
+ "spider mandibles" = GLOB.spider_mandibles_list,
"caps" = GLOB.caps_list,
"ipc_screen" = GLOB.ipc_screens_list,
"ipc_antenna" = GLOB.ipc_antennas_list,
@@ -274,7 +280,7 @@ GLOBAL_LIST_INIT(roundstart_tongues, list("default","human tongue" = /obj/item/o
//locked parts are those that your picked species requires to have
//unlocked parts are those that anyone can choose on customisation regardless
//parts not in unlocked, but in all, are thus locked
-GLOBAL_LIST_INIT(all_mutant_parts, list("tail_lizard" = "Tail", "mam_tail" = "Tail", "tail_human" = "Tail", "snout" = "Snout", "frills" = "Frills", "spines" = "Spines", "body_markings" = "Body Markings", "mam_body_markings" = "Species Markings" , "mam_ears" = "Ears", "ears" = "Ears", "mam_snouts" = "Snout", "legs" = "Legs", "deco_wings" = "Decorative Wings", "insect_wings" = "Insect Wings", "insect_fluff" = "Insect Fluff", "taur" = "Tauric Body", "insect_markings" = "Insect Markings", "wings" = "Wings", "xenohead" = "Caste Head", "xenotail" = "Tail", "xenodorsal" = "Dorsal Spines", "ipc_screen" = "Screen", "ipc_antenna" = "Antenna", "meat_type" = "Meat Type", "horns" = "Horns"))
+GLOBAL_LIST_INIT(all_mutant_parts, list("tail_lizard" = "Tail", "mam_tail" = "Tail", "tail_human" = "Tail", "snout" = "Snout", "frills" = "Frills", "spines" = "Spines", "body_markings" = "Body Markings", "mam_body_markings" = "Species Markings" , "mam_ears" = "Ears", "ears" = "Ears", "mam_snouts" = "Snout", "legs" = "Legs", "deco_wings" = "Decorative Wings", "insect_wings" = "Insect Wings", "insect_fluff" = "Insect Fluff", "taur" = "Tauric Body", "insect_markings" = "Insect Markings", "wings" = "Wings", "spider_legs" = "Spider Legs", "spider_spinneret" = "Spinneret", "spider_mandibles" = "Mandibles", "xenohead" = "Caste Head", "xenotail" = "Tail", "xenodorsal" = "Dorsal Spines", "ipc_screen" = "Screen", "ipc_antenna" = "Antenna", "meat_type" = "Meat Type", "horns" = "Horns"))
GLOBAL_LIST_INIT(unlocked_mutant_parts, list("horns", "insect_fluff"))
//parts in either of the above two lists that require a second option that allows them to be coloured
GLOBAL_LIST_INIT(colored_mutant_parts, list("insect_wings" = "wings_color", "deco_wings" = "wings_color", "horns" = "horns_color"))
diff --git a/code/_globalvars/lists/names.dm b/code/_globalvars/lists/names.dm
index b80fcf0bbf..297902ac87 100644
--- a/code/_globalvars/lists/names.dm
+++ b/code/_globalvars/lists/names.dm
@@ -16,6 +16,8 @@ GLOBAL_LIST_INIT(carp_names, world.file2list("strings/names/carp.txt"))
GLOBAL_LIST_INIT(golem_names, world.file2list("strings/names/golem.txt"))
GLOBAL_LIST_INIT(moth_first, world.file2list("strings/names/moth_first.txt"))
GLOBAL_LIST_INIT(moth_last, world.file2list("strings/names/moth_last.txt"))
+GLOBAL_LIST_INIT(spider_first, world.file2list("strings/names/spider_first.txt"))
+GLOBAL_LIST_INIT(spider_last, world.file2list("strings/names/spider_last.txt"))
GLOBAL_LIST_INIT(plasmaman_names, world.file2list("strings/names/plasmaman.txt"))
GLOBAL_LIST_INIT(ethereal_names, world.file2list("strings/names/ethereal.txt"))
GLOBAL_LIST_INIT(posibrain_names, world.file2list("strings/names/posibrain.txt"))
diff --git a/code/controllers/subsystem/traumas.dm b/code/controllers/subsystem/traumas.dm
index f556f7dee7..174c298470 100644
--- a/code/controllers/subsystem/traumas.dm
+++ b/code/controllers/subsystem/traumas.dm
@@ -198,7 +198,8 @@ SUBSYSTEM_DEF(traumas)
"aliens" = typecacheof(list(/datum/species/abductor, /datum/species/jelly, /datum/species/pod, /datum/species/shadow)),
"anime" = typecacheof(list(/datum/species/human/felinid)),
"cats" = typecacheof(list(/datum/species/human/felinid)),
- "syndicate" = typecacheof(list(/datum/species/corporate, /datum/species/zombie/infectious))
+ "syndicate" = typecacheof(list(/datum/species/corporate, /datum/species/zombie/infectious)),
+ "spiders" = typecacheof(list(/datum/species/spider))
)
return ..()
diff --git a/code/game/objects/effects/spiderperson_web.dm b/code/game/objects/effects/spiderperson_web.dm
new file mode 100644
index 0000000000..93d254d672
--- /dev/null
+++ b/code/game/objects/effects/spiderperson_web.dm
@@ -0,0 +1,70 @@
+/obj/structure/spider_player
+ name = "large web"
+ icon = 'icons/effects/effects.dmi'
+ desc = "It's stringy and sticky, but the threads are larger than what spiderlings could produce."
+ anchored = TRUE
+ density = FALSE
+ max_integrity = 20
+
+/obj/structure/spider_player/New()
+ ..()
+ icon_state = pick(list("stickyweb1", "stickyweb2"))
+
+
+/obj/structure/spider_player/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
+ if(damage_type == BURN)//the stickiness of the web mutes all attack sounds except fire damage type
+ playsound(loc, 'sound/items/welder.ogg', 100, TRUE)
+
+/obj/structure/spider_player/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir)
+ if(damage_flag == "melee")
+ switch(damage_type)
+ if(BURN)
+ damage_amount *= 2
+ if(BRUTE)
+ damage_amount *= 0.5
+ . = ..()
+
+/obj/structure/spider_player/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
+ if(exposed_temperature > 300)
+ take_damage(5, BURN, 0, 0)
+
+/obj/structure/spider_player/CanPass(atom/movable/mover, turf/target)
+ . = ..()
+ if(isspiderperson(mover))
+
+ return TRUE
+ else if(isliving(mover))
+ if(isspiderperson(mover.pulledby))
+
+ return TRUE
+ if(prob(50))
+ to_chat(mover, "You get stuck in \the [src] for a moment.")
+ return FALSE
+
+/obj/structure/spider_player/cocoon
+ name = "cocoon"
+ desc = "Something wrapped in silky spider web."
+ icon_state = "cocoon1"
+ anchored = FALSE
+ density = FALSE
+ max_integrity = 60
+
+/obj/structure/spider_player/cocoon/Initialize()
+ icon_state = pick("cocoon1","cocoon2","cocoon3")
+ . = ..()
+
+/obj/structure/spider_player/cocoon/container_resist(mob/living/user)
+ var/breakout_time = 1000 // DECI not DECA ffs
+ to_chat(user, "You struggle against the tight bonds... (This will take about [DisplayTimeText(breakout_time)].)")
+ visible_message("You see something struggling and writhing in \the [src]!")
+ if(do_after(user,(breakout_time), target = src))
+ if(!user || user.stat != CONSCIOUS || user.loc != src)
+ return
+ qdel(src)
+
+/obj/structure/spider_player/cocoon/Destroy()
+ var/turf/T = get_turf(src)
+ src.visible_message("\The [src] splits open.")
+ for(var/atom/movable/A in contents)
+ A.forceMove(T)
+ return ..()
diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm
index e287d9b1b3..81740fd71a 100644
--- a/code/modules/admin/create_mob.dm
+++ b/code/modules/admin/create_mob.dm
@@ -43,6 +43,9 @@
H.dna.features["insect_wings"] = pick(GLOB.insect_wings_list)
H.dna.features["deco_wings"] = pick(GLOB.deco_wings_list)
H.dna.features["insect_fluff"] = pick(GLOB.insect_fluffs_list)
+ H.dna.features["spider_legs"] = pick(GLOB.spider_legs_list)
+ H.dna.features["spider_spinneret"] = pick(GLOB.spider_spinneret_list)
+ H.dna.features["spider_mandibles"] = pick(GLOB.spider_mandibles_list)
H.dna.features["flavor_text"] = "" //Oh no.
H.dna.features["body_model"] = H.gender
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index a673012118..41ed49f848 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -118,6 +118,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
"insect_wings" = "Plain",
"insect_fluff" = "None",
"insect_markings" = "None",
+ "spider_legs" = "Plain",
+ "spider_spinneret" = "Plain",
+ "spider_mandibles" = "Plain",
"mam_body_markings" = "Plain",
"mam_ears" = "None",
"mam_snouts" = "None",
@@ -1681,6 +1684,24 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(new_ipc_antenna)
features["ipc_antenna"] = new_ipc_antenna
+ if("spider_legs")
+ var/new_spider_legs
+ new_spider_legs = input(user, "Choose your character's variant of spider legs:", "Character Preference") as null|anything in GLOB.spider_legs_list
+ if(new_spider_legs)
+ features["spider_legs"] = new_spider_legs
+
+ if("spider_spinneret")
+ var/new_spider_spinneret
+ new_spider_spinneret = input(user, "Choose your character's spinneret markings:", "Character Preference") as null|anything in GLOB.spider_spinneret_list
+ if(new_spider_spinneret)
+ features["spider_spinneret"] = new_spider_spinneret
+
+ if("spider_mandibles")
+ var/new_spider_mandibles
+ new_spider_mandibles = input(user, "Choose your character's variant of mandibles:", "Character Preference") as null|anything in GLOB.spider_mandibles_list
+ if (new_spider_mandibles)
+ features["spider_mandibles"] = new_spider_mandibles
+
if("tail_lizard")
var/new_tail
new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in GLOB.tails_list_lizard
@@ -1846,6 +1867,24 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(new_insect_markings)
features["insect_markings"] = new_insect_markings
+ if("spider_legs")
+ var/new_spider_legs
+ new_spider_legs = input(user, "Choose your character's variant of spider legs:", "Character Preference") as null|anything in GLOB.spider_legs_list
+ if(new_spider_legs)
+ features["spider_legs"] = new_spider_legs
+
+ if("spider_spinneret")
+ var/new_spider_spinneret
+ new_spider_spinneret = input(user, "Choose your character's spinneret markings:", "Character Preference") as null|anything in GLOB.spider_spinneret_list
+ if(new_spider_spinneret)
+ features["spider_spinneret"] = new_spider_spinneret
+
+ if("spider_mandibles")
+ var/new_spider_mandibles
+ new_spider_mandibles = input(user, "Choose your character's variant of mandibles:", "Character Preference") as null|anything in GLOB.spider_mandibles_list
+ if (new_spider_mandibles)
+ features["spider_mandibles"] = new_spider_mandibles
+
if("s_tone")
var/list/choices = GLOB.skin_tones - GLOB.nonstandard_skin_tones
if(CONFIG_GET(flag/allow_custom_skintones))
@@ -1885,6 +1924,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
features["xenotail"] = "None"
features["tail_human"] = "None"
features["tail_lizard"] = "None"
+ features["spider_spinneret"] = "None"
if("ears")
var/list/snowflake_ears_list = list()
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 23d0288163..8fc0ea8c04 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -511,6 +511,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["feature_insect_wings"] >> features["insect_wings"]
S["feature_insect_fluff"] >> features["insect_fluff"]
S["feature_insect_markings"] >> features["insect_markings"]
+ S["feature_spider_legs"] >> features["spider_legs"]
+ S["feature_spider_spinneret"] >> features["spider_spinneret"]
+ S["feature_spider_mandibles"] >> features["spider_mandibles"]
S["feature_horns_color"] >> features["horns_color"]
S["feature_wings_color"] >> features["wings_color"]
S["persistent_scars"] >> persistent_scars
@@ -690,6 +693,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
features["insect_fluff"] = sanitize_inlist(features["insect_fluff"], GLOB.insect_fluffs_list)
features["insect_markings"] = sanitize_inlist(features["insect_markings"], GLOB.insect_markings_list, "None")
features["insect_wings"] = sanitize_inlist(features["insect_wings"], GLOB.insect_wings_list)
+ features["spider_legs"] = sanitize_inlist(features["spider_legs"], GLOB.spider_legs_list, "Plain")
+ features["spider_spinneret"] = sanitize_inlist(features["spider_spinneret"], GLOB.spider_spinneret_list, "Plain")
+ features["spider_mandibles"] = sanitize_inlist(features["spider_mandibles"], GLOB.spider_mandibles_list, "Plain")
var/static/size_min
if(!size_min)
@@ -828,6 +834,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["feature_insect_wings"] , features["insect_wings"])
WRITE_FILE(S["feature_insect_fluff"] , features["insect_fluff"])
WRITE_FILE(S["feature_insect_markings"] , features["insect_markings"])
+ WRITE_FILE(S["feature_spider_legs"] , features["spider_legs"])
+ WRITE_FILE(S["feature_spider_spinneret"] , features["spider_spinneret"])
+ WRITE_FILE(S["feature_spider_mandibles"] , features["spider_mandibles"])
WRITE_FILE(S["feature_meat"] , features["meat_type"])
WRITE_FILE(S["feature_has_cock"], features["has_cock"])
diff --git a/code/modules/language/language_holder.dm b/code/modules/language/language_holder.dm
index c1677117e9..77d03caa13 100644
--- a/code/modules/language/language_holder.dm
+++ b/code/modules/language/language_holder.dm
@@ -330,6 +330,12 @@ Key procs
spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
/datum/language/voltaic = list(LANGUAGE_ATOM))
+/datum/language_holder/spider
+ understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/spider = list(LANGUAGE_ATOM))
+ spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/spider = list(LANGUAGE_ATOM))
+
/datum/language_holder/empty
understood_languages = list()
spoken_languages = list()
diff --git a/code/modules/language/spider.dm b/code/modules/language/spider.dm
new file mode 100644
index 0000000000..43ab5e0de3
--- /dev/null
+++ b/code/modules/language/spider.dm
@@ -0,0 +1,13 @@
+/datum/language/spider
+ name = "Rachnidian"
+ desc = "A language that exploits the multiple limbs of spiders to do subtle dance like movements to communicate.\
+ A proper speaker's movements are quick and sharp enough to make audible whiffs and thumps however, which are intelligible over the radio."
+ speech_verb = "chitter"
+ ask_verb = "chitter"
+ exclaim_verb = "chitter"
+ key = "r"
+ flags = NO_STUTTER | LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD
+
+/datum/language/spider/scramble(input)
+ . = prob(65) ? "wiff" : "thump"
+ . += (copytext(input, length(input)) == "?") ? "?" : "!"
diff --git a/code/modules/mob/dead/new_player/sprite_accessories/spider.dm b/code/modules/mob/dead/new_player/sprite_accessories/spider.dm
new file mode 100644
index 0000000000..dcfa9aa45f
--- /dev/null
+++ b/code/modules/mob/dead/new_player/sprite_accessories/spider.dm
@@ -0,0 +1,47 @@
+/datum/sprite_accessory/spider_legs
+ icon = 'icons/mob/spider_legs.dmi'
+ color_src = MUTCOLORS
+
+/datum/sprite_accessory/spider_legs/plain
+ name = "Plain"
+ icon_state = "plain"
+
+/datum/sprite_accessory/spider_legs/fuzzy
+ name = "Fuzzy"
+ icon_state = "fuzzy"
+
+/datum/sprite_accessory/spider_legs/spiky
+ name = "Spiky"
+ icon_state = "spiky"
+
+/datum/sprite_accessory/spider_spinneret
+ icon = 'icons/mob/spider_spinneret.dmi'
+ color_src = MUTCOLORS
+
+/datum/sprite_accessory/spider_spinneret/plain
+ name = "Plain"
+ icon_state = "plain"
+
+/datum/sprite_accessory/spider_spinneret/fuzzy
+ name = "Fuzzy"
+ icon_state = "fuzzy"
+
+/datum/sprite_accessory/spider_spinneret/black_widow
+ name = "Black Widow"
+ icon_state = "blackwidow"
+
+/datum/sprite_accessory/spider_mandibles
+ icon = 'icons/mob/spider_mandibles.dmi'
+ color_src = MUTCOLORS
+
+/datum/sprite_accessory/spider_mandibles/plain
+ name = "Plain"
+ icon_state = "plain"
+
+/datum/sprite_accessory/spider_mandibles/fuzzy
+ name = "Fuzzy"
+ icon_state = "fuzzy"
+
+/datum/sprite_accessory/spider_mandibles/spiky
+ name = "Spiky"
+ icon_state = "spiky"
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 389787df67..b3f2f923a0 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -1255,3 +1255,6 @@
/mob/living/carbon/human/species/roundstartslime
race = /datum/species/jelly/roundstartslime
+
+/mob/living/carbon/human/species/spider
+ race = /datum/species/spider
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index d433ca1821..648b0921a9 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -117,6 +117,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
//the ids you can use for your species, if empty, it means default only and not changeable
var/list/allowed_limb_ids
+ var/override_bp_icon //override for the icon path used when setting bodypart overlays. Intended for species that don't fit in the standard 32x32 files.
+
///////////
// PROCS //
///////////
diff --git a/code/modules/mob/living/carbon/human/species_types/spider.dm b/code/modules/mob/living/carbon/human/species_types/spider.dm
new file mode 100644
index 0000000000..e93375b83e
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species_types/spider.dm
@@ -0,0 +1,156 @@
+/datum/species/spider
+ name = "Arachnid"
+ id = "rachnid"
+ override_bp_icon = 'icons/mob/spider_parts.dmi'
+ say_mod = "chitters"
+ default_color = "00FF00"
+ species_traits = list(LIPS, NOEYES, NO_UNDERWEAR)
+ inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BUG
+ mutant_bodyparts = list("spider_legs", "spider_spinneret", "spider_mandibles")
+ attack_verb = "slash"
+ attack_sound = 'sound/weapons/slash.ogg'
+ miss_sound = 'sound/weapons/slashmiss.ogg'
+ meat = /obj/item/reagent_containers/food/snacks/meat/slab/spider
+ liked_food = MEAT | RAW
+ disliked_food = FRUIT | GROSS
+ toxic_food = VEGETABLES | DAIRY
+ mutanteyes = /obj/item/organ/eyes/night_vision/spider
+ mutanttongue = /obj/item/organ/tongue/spider
+ species_language_holder = /datum/language_holder/spider
+ var/web_cooldown = 30
+ var/web_ready = TRUE
+ var/spinner_rate = 75
+
+/datum/species/spider/random_name(gender,unique,lastname)
+ if(unique)
+ return random_unique_spider_name()
+
+ var/randname = spider_name()
+
+ if(lastname)
+ randname += " [lastname]"
+
+ return randname
+
+/datum/species/spider/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H)
+ . = ..()
+ if(chem.type == /datum/reagent/toxin/pestkiller)
+ H.adjustToxLoss(3)
+ H.reagents.remove_reagent(chem.type, REAGENTS_METABOLISM)
+
+/datum/species/spider/check_weakness(obj/item/weapon, mob/living/attacker)
+ if(istype(weapon, /obj/item/melee/flyswatter))
+ return 9 //flyswatters deal 10x damage to spiders
+ return 0
+
+/datum/species/spider/on_species_gain(mob/living/carbon/human/H, datum/species/old_species)
+ . = ..()
+ var/datum/action/innate/spin_web/SW = new
+ var/datum/action/innate/spin_cocoon/SC = new
+ SC.Grant(H)
+ SW.Grant(H)
+
+/datum/species/spider/on_species_loss(mob/living/carbon/human/H)
+ . = ..()
+ var/datum/action/innate/spin_web/SW = locate(/datum/action/innate/spin_web) in H.actions
+ var/datum/action/innate/spin_cocoon/SC = locate(/datum/action/innate/spin_cocoon) in H.actions
+ SC?.Remove(H)
+ SW?.Remove(H)
+
+/datum/action/innate/spin_web
+ name = "Spin Web"
+ check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_CONSCIOUS
+ icon_icon = 'icons/mob/actions/actions_animal.dmi'
+ button_icon_state = "lay_web"
+
+/datum/action/innate/spin_cocoon
+ name = "Spin Cocoon"
+ check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUN|AB_CHECK_CONSCIOUS
+ icon_icon = 'icons/mob/actions/actions_animal.dmi'
+ button_icon_state = "wrap_0"
+
+/datum/action/innate/spin_web/Activate()
+ var/mob/living/carbon/human/species/spider/H = owner
+ var/datum/species/spider/E = H.dna.species
+ if(H.stat == "DEAD")
+ return
+ if(E.web_ready == FALSE)
+ to_chat(H, "You need to wait awhile to regenerate web fluid.")
+ return
+ var/turf/T = get_turf(H)
+ if(!T)
+ to_chat(H, "There's no room to spin your web here!")
+ return
+ var/obj/structure/spider/stickyweb/W = locate() in T
+ var/obj/structure/spider_player/W2 = locate() in T
+ if(W || W2)
+ to_chat(H, "There's already a web here!")
+ return
+ // Should have some minimum amount of food before trying to activate
+ var/nutrition_threshold = NUTRITION_LEVEL_FED
+ if (H.nutrition >= nutrition_threshold)
+ to_chat(H, "You begin spinning some web...")
+ if(!do_after(H, 10 SECONDS, 1, T))
+ to_chat(H, "Your web spinning was interrupted!")
+ return
+ if(prob(75))
+ H.adjust_nutrition(-E.spinner_rate)
+ addtimer(VARSET_CALLBACK(E, web_ready, TRUE), E.web_cooldown)
+ to_chat(H, "You use up a fair amount of energy spinning the web.")
+ new /obj/structure/spider_player(T, owner)
+ to_chat(H, "You weave a web on the ground with your spinneret!")
+
+ else
+ to_chat(H, "You're too hungry to spin web right now, eat something first!")
+ return
+/*
+ This took me far too long to figure out so I'm gonna document it here.
+ 1) Create an innate action for the species
+ 2) Have that action trigger a RegisterSignal for mob clicking
+ 3) Trigger the cocoonAtom proc on that signal
+ 4) Validate the target then start spinning
+ 5) if you're not interrupted, force move the target to the cocoon created at their location.
+*/
+/datum/action/innate/spin_cocoon/Activate()
+ var/mob/living/carbon/human/species/spider/H = owner
+ var/datum/species/spider/E = H.dna.species
+ if(H.stat == "DEAD")
+ return
+ if(E.web_ready == FALSE)
+ to_chat(H, "You need to wait awhile to regenerate web fluid.")
+ return
+ var/nutrition_threshold = NUTRITION_LEVEL_FED
+ if (H.nutrition >= nutrition_threshold)
+ to_chat(H, "You pull out a strand from your spinneret, ready to wrap a target.
\
+ (Press ALT+CLICK or MMB on the target to start wrapping.)")
+ H.adjust_nutrition(E.spinner_rate * -0.5)
+ addtimer(VARSET_CALLBACK(E, web_ready, TRUE), E.web_cooldown)
+ RegisterSignal(H, list(COMSIG_MOB_ALTCLICKON), .proc/cocoonAtom)
+ return
+ else
+ to_chat(H, "You're too hungry to spin web right now, eat something first!")
+ return
+
+/datum/action/innate/spin_cocoon/proc/cocoonAtom(mob/living/carbon/human/species/spider/H, atom/movable/A)
+ UnregisterSignal(H, list(COMSIG_MOB_ALTCLICKON))
+ var/datum/species/spider/E = H.dna.species
+ if (!H || !isspiderperson(H))
+ return COMSIG_MOB_CANCEL_CLICKON
+ else
+ if(E.web_ready == FALSE)
+ to_chat(H, "You need to wait awhile to regenerate web fluid.")
+ return
+ if(!do_after(H, 10 SECONDS, 1, A))
+ to_chat(H, "Your web spinning was interrupted!")
+ return
+ H.adjust_nutrition(E.spinner_rate * -3)
+ var/obj/structure/spider_player/cocoon/C = new(A.loc)
+ if(isliving(A))
+ C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3")
+ A.forceMove(C)
+ H.visible_message("[H] wraps [A] into a large cocoon!")
+ return
+ else
+ A.forceMove(C)
+ H.visible_message("[H] wraps [A] into a cocoon!")
+ return
\ No newline at end of file
diff --git a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
index 289b92f06d..b3416f831b 100644
--- a/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/alcohol_reagents.dm
@@ -1962,7 +1962,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
/datum/reagent/consumable/ethanol/bug_spray/on_mob_life(mob/living/carbon/M)
//Bugs should not drink Bug spray.
- if(isinsect(M) || isflyperson(M))
+ if(isinsect(M) || isflyperson(M) || isspiderperson(M))
M.adjustToxLoss(1,0)
return ..()
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 86f8b341d9..e70119f541 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -753,6 +753,12 @@
race = /datum/species/lizard/ashwalker
mutationtext = "The pain subsides. You feel... savage."
+/datum/reagent/mutationtoxin/arachnid
+ name = "Arachnid Mutation Toxin"
+ description = "A glowing toxin."
+ color = "#5EFF3B" //RGB: 94, 255, 59
+ race = /datum/species/spider
+ mutationtext = "The pain subsides. You feel... silky."
//DANGEROUS RACES
/datum/reagent/mutationtoxin/shadow
diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm
index 92861a94ed..ae16bf389d 100644
--- a/code/modules/reagents/chemistry/recipes/others.dm
+++ b/code/modules/reagents/chemistry/recipes/others.dm
@@ -715,21 +715,27 @@
required_reagents = list(/datum/reagent/toxin/mindbreaker = 1, /datum/reagent/medicine/synaptizine = 1, /datum/reagent/water = 1)
/datum/chemical_reaction/cat
- name = "felinid mutation toxic"
+ name = "felinid mutation toxin"
id = /datum/reagent/mutationtoxin/felinid
results = list(/datum/reagent/mutationtoxin/felinid = 1)
required_reagents = list(/datum/reagent/toxin/mindbreaker = 1, /datum/reagent/ammonia = 1, /datum/reagent/water = 1, /datum/reagent/pax/catnip = 1, /datum/reagent/mutationtoxin = 1)
required_temp = 450
/datum/chemical_reaction/moff
- name = "insect mutation toxic"
+ name = "insect mutation toxin"
id = /datum/reagent/mutationtoxin/insect
results = list(/datum/reagent/mutationtoxin/insect = 1)
required_reagents = list(/datum/reagent/liquid_dark_matter = 2, /datum/reagent/ammonia = 5, /datum/reagent/lithium = 1, /datum/reagent/mutationtoxin = 1)
required_temp = 320
+/datum/chemical_reaction/mutationtoxin/arachnid
+ name = "spiderperson mutation toxin"
+ id = /datum/reagent/mutationtoxin/arachnid
+ results = list(/datum/reagent/mutationtoxin/arachnid = 1)
+ required_reagents = list(/datum/reagent/mutationtoxin/insect = 1, /datum/reagent/toxin/heparin = 10)
+
/datum/chemical_reaction/notlight //Harder to make do to it being a hard race to play
- name = "shadow muatatuin toxic"
+ name = "shadow mutation toxin"
id = /datum/reagent/mutationtoxin/shadow
results = list(/datum/reagent/mutationtoxin/shadow = 1)
required_reagents = list(/datum/reagent/liquid_dark_matter = 5, /datum/reagent/medicine/synaptizine = 10, /datum/reagent/medicine/oculine = 10, /datum/reagent/mutationtoxin = 1)
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index e90f86bf1c..1ebbabe1e7 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -651,8 +651,11 @@
body_markings = null
aux_marking = null
- if(species_id in GLOB.greyscale_limb_types) //should they have greyscales?
- base_bp_icon = DEFAULT_BODYPART_ICON_ORGANIC
+ if(S.override_bp_icon)
+ base_bp_icon = S.override_bp_icon
+ else
+ if(species_id in GLOB.greyscale_limb_types) //should they have greyscales?
+ base_bp_icon = DEFAULT_BODYPART_ICON_ORGANIC
if(base_bp_icon != DEFAULT_BODYPART_ICON)
color_src = mut_colors ? MUTCOLORS : ((H.dna.skin_tone_override && S.use_skintones == USE_SKINTONES_GRAYSCALE_CUSTOM) ? CUSTOM_SKINTONE : SKINTONE)
diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm
index e3ab44213f..abc563839b 100644
--- a/code/modules/surgery/organs/eyes.dm
+++ b/code/modules/surgery/organs/eyes.dm
@@ -421,6 +421,12 @@
name = "ipc eyes"
icon_state = "cybernetic_eyeballs"
+/obj/item/organ/eyes/night_vision/spider
+ name = "spider eyes"
+ desc = "These eyes seem to have increased sensitivity to bright light, offset by basic night vision."
+ see_in_dark = 4
+ flash_protect = -1
+
#undef BLURRY_VISION_ONE
#undef BLURRY_VISION_TWO
#undef BLIND_VISION_THREE
diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm
index 929b42f96d..fc534343ac 100644
--- a/code/modules/surgery/organs/tongue.dm
+++ b/code/modules/surgery/organs/tongue.dm
@@ -258,3 +258,22 @@
/obj/item/organ/tongue/ethereal/Initialize(mapload)
. = ..()
languages_possible = languages_possible_ethereal
+
+/obj/item/organ/tongue/spider
+ name = "inner mandible"
+ desc = "A set of soft, spoon-esque mandibles closer to the mouth opening, that allow for basic speech, and the ability to speak Rachnidian."
+ say_mod = "chitters"
+ var/static/list/languages_possible_arachnid = typecacheof(list(
+ /datum/language/common,
+ /datum/language/draconic,
+ /datum/language/codespeak,
+ /datum/language/monkey,
+ /datum/language/narsie,
+ /datum/language/beachbum,
+ /datum/language/aphasia,
+ /datum/language/spider,
+ ))
+
+/obj/item/organ/tongue/spider/Initialize(mapload)
+ . = ..()
+ languages_possible = languages_possible_arachnid
\ No newline at end of file
diff --git a/config/game_options.txt b/config/game_options.txt
index a5b0d0b8c4..38b632175f 100644
--- a/config/game_options.txt
+++ b/config/game_options.txt
@@ -477,6 +477,7 @@ ROUNDSTART_RACES xeno
ROUNDSTART_RACES slimeperson
ROUNDSTART_RACES guilmon
ROUNDSTART_RACES ipc
+ROUNDSTART_RACES rachnid
##-------------------------------------------------------------------------------------------
diff --git a/icons/mob/spider_legs.dmi b/icons/mob/spider_legs.dmi
new file mode 100644
index 0000000000..d04b5a719e
Binary files /dev/null and b/icons/mob/spider_legs.dmi differ
diff --git a/icons/mob/spider_mandibles.dmi b/icons/mob/spider_mandibles.dmi
new file mode 100644
index 0000000000..81b4b29a12
Binary files /dev/null and b/icons/mob/spider_mandibles.dmi differ
diff --git a/icons/mob/spider_parts.dmi b/icons/mob/spider_parts.dmi
new file mode 100644
index 0000000000..389ca79dda
Binary files /dev/null and b/icons/mob/spider_parts.dmi differ
diff --git a/icons/mob/spider_spinneret.dmi b/icons/mob/spider_spinneret.dmi
new file mode 100644
index 0000000000..36dcb6714c
Binary files /dev/null and b/icons/mob/spider_spinneret.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 143f66a656..0e31cf38fa 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -808,8 +808,6 @@
#include "code\game\machinery\mass_driver.dm"
#include "code\game\machinery\navbeacon.dm"
#include "code\game\machinery\PDApainter.dm"
-#include "code\game\machinery\poweredfans\fan_assembly.dm"
-#include "code\game\machinery\poweredfans\poweredfans.dm"
#include "code\game\machinery\quantum_pad.dm"
#include "code\game\machinery\recharger.dm"
#include "code\game\machinery\rechargestation.dm"
@@ -887,6 +885,8 @@
#include "code\game\machinery\porta_turret\portable_turret.dm"
#include "code\game\machinery\porta_turret\portable_turret_construct.dm"
#include "code\game\machinery\porta_turret\portable_turret_cover.dm"
+#include "code\game\machinery\poweredfans\fan_assembly.dm"
+#include "code\game\machinery\poweredfans\poweredfans.dm"
#include "code\game\machinery\shuttle\custom_shuttle.dm"
#include "code\game\machinery\shuttle\shuttle_engine.dm"
#include "code\game\machinery\shuttle\shuttle_heater.dm"
@@ -956,6 +956,7 @@
#include "code\game\objects\effects\overlays.dm"
#include "code\game\objects\effects\portals.dm"
#include "code\game\objects\effects\proximity.dm"
+#include "code\game\objects\effects\spiderperson_web.dm"
#include "code\game\objects\effects\spiders.dm"
#include "code\game\objects\effects\step_triggers.dm"
#include "code\game\objects\effects\wanted_poster.dm"
@@ -2305,6 +2306,7 @@
#include "code\modules\language\narsian.dm"
#include "code\modules\language\ratvarian.dm"
#include "code\modules\language\slime.dm"
+#include "code\modules\language\spider.dm"
#include "code\modules\language\swarmer.dm"
#include "code\modules\language\sylvan.dm"
#include "code\modules\language\vampiric.dm"
@@ -2420,6 +2422,7 @@
#include "code\modules\mob\dead\new_player\sprite_accessories\legs_and_taurs.dm"
#include "code\modules\mob\dead\new_player\sprite_accessories\snouts.dm"
#include "code\modules\mob\dead\new_player\sprite_accessories\socks.dm"
+#include "code\modules\mob\dead\new_player\sprite_accessories\spider.dm"
#include "code\modules\mob\dead\new_player\sprite_accessories\spines.dm"
#include "code\modules\mob\dead\new_player\sprite_accessories\synthliz.dm"
#include "code\modules\mob\dead\new_player\sprite_accessories\tails.dm"
@@ -2556,6 +2559,7 @@
#include "code\modules\mob\living\carbon\human\species_types\podpeople.dm"
#include "code\modules\mob\living\carbon\human\species_types\shadowpeople.dm"
#include "code\modules\mob\living\carbon\human\species_types\skeletons.dm"
+#include "code\modules\mob\living\carbon\human\species_types\spider.dm"
#include "code\modules\mob\living\carbon\human\species_types\synthliz.dm"
#include "code\modules\mob\living\carbon\human\species_types\synths.dm"
#include "code\modules\mob\living\carbon\human\species_types\vampire.dm"