-
-
Notifications
You must be signed in to change notification settings - Fork 444
/
Copy path_species.dm
2985 lines (2605 loc) · 119 KB
/
_species.dm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// This code handles different species in the game.
GLOBAL_LIST_EMPTY(roundstart_races)
GLOBAL_LIST_EMPTY(mentor_races)
/// An assoc list of species types to their features (from get_features())
GLOBAL_LIST_EMPTY(features_by_species)
/datum/species
/// if the game needs to manually check your race to do something not included in a proc here, it will use this
var/id
///this is used if you want to use a different species limb sprites. Mainly used for angels as they look like humans.
var/limbs_id
/// this is the fluff name. these will be left generic (such as 'Lizardperson' for the lizard race) so servers can change them to whatever
var/name
/// The formatting of the name of the species in plural context. Defaults to "[name]\s" if unset.
/// Ex "[Plasmamen] are weak", "[Mothmen] are strong", "[Lizardpeople] don't like", "[Golems] hate"
var/plural_form
/// if alien colors are disabled, this is the color that will be used by that race
var/default_color = "#FFF"
/// The icon this species uses on the crew monitor.
var/monitor_icon
/// The color of the icon this species uses on the crew monitor.
var/monitor_color = "#FFFFFF"
///A list that contains pixel offsets for various clothing features, if your species is a different shape
var/list/offset_features = list(OFFSET_UNIFORM = list(0,0), OFFSET_ID = list(0,0), OFFSET_GLOVES = list(0,0), OFFSET_GLASSES = list(0,0), OFFSET_EARS = list(0,0), OFFSET_SHOES = list(0,0), OFFSET_S_STORE = list(0,0), OFFSET_FACEMASK = list(0,0), OFFSET_HEAD = list(0,0), OFFSET_FACE = list(0,0), OFFSET_BELT = list(0,0), OFFSET_BACK = list(0,0), OFFSET_SUIT = list(0,0), OFFSET_NECK = list(0,0))
/// this allows races to have specific hair colors... if null, it uses the H's hair/facial hair colors. if "mutcolor", it uses the H's mutant_color
var/hair_color
/// the alpha used by the hair. 255 is completely solid, 0 is transparent.
var/hair_alpha = 255
///The gradient style used for the mob's hair.
var/grad_style
///The gradient color used to color the gradient.
var/grad_color
/// does it use skintones or not? (spoiler alert this is only used by humans)
var/use_skintones = FALSE
var/icon_husk
var/forced_skintone
/// What genders can this race be?
var/list/possible_genders = list(MALE, PLURAL, FEMALE)
/// If your race wants to bleed something other than bog standard blood, change this to reagent id.
var/datum/reagent/exotic_blood
///If your race uses a non standard bloodtype (A+, O-, AB-, etc)
var/exotic_bloodtype = ""
///What the species drops on gibbing
var/meat = /obj/item/reagent_containers/food/snacks/meat/slab/human
///What, if any, leather will be dropped
var/skinned_type
///What kind of foods the species loves
var/liked_food = NONE
///What kind of foods the species dislikes!
var/disliked_food = GROSS
///What kind of foods cause harm to the species
var/toxic_food = TOXIC
/// slots the race can't equip stuff to
var/list/no_equip = list()
/// slots the race can't equip stuff to that have been added externally that should be inherited on species change
var/list/extra_no_equip = list()
/// this is sorta... weird. it basically lets you equip stuff that usually needs jumpsuits without one, like belts and pockets and ids
var/nojumpsuit = FALSE
/// affects the speech message
var/say_mod = "says"
/// Weighted list. NOTE: Picks one of the list component and then does a prob() on it, since we can't do a proper weighted pick, since we need to take into account the regular say_mod.
//TL;DR "meows" = 75 and "rawr" = 25 isn't actually 75% and 25%. It's 75% * 50% = 37.5% and 25% * 50% = 12.5%. Chance is divided by number of elements
var/list/rare_say_mod = list()
///Used if you want to give your species thier own language
var/species_language_holder = /datum/language_holder
/// Default mutant bodyparts for this species. Don't forget to set one for every mutant bodypart you allow this species to have.
var/list/default_features = list()
/// Visible CURRENT bodyparts that are unique to a species. DO NOT USE THIS AS A LIST OF ALL POSSIBLE BODYPARTS AS IT WILL FUCK SHIT UP! Changes to this list for non-species specific bodyparts (ie cat ears and tails) should be assigned at organ level if possible. Layer hiding is handled by handle_mutant_bodyparts() below.
var/list/mutant_bodyparts = list()
///Internal organs that are unique to this race.
var/list/mutant_organs = list()
/// this affects the race's speed. positive numbers make it move slower, negative numbers make it move faster
var/speedmod = 0
///overall defense for the race... or less defense, if it's negative.
var/armor = 0
/// multiplier for brute damage
var/brutemod = 1
/// multiplier for burn damage
var/burnmod = 1
/// multiplier for cold damage
var/coldmod = 1
/// multiplier for heat damage
var/heatmod = 1
/// multiplier for temperature adjustment
var/tempmod = 1
/// multiplier for acid damage // yogs - Old Plant People
var/acidmod = 1
/// multiplier for stun duration
var/stunmod = 1
/// multiplier for oxyloss
var/oxymod = 1
/// multiplier for cloneloss
var/clonemod = 1
/// multiplier for toxloss
var/toxmod = 1
/// multiplier for stun duration
var/staminamod = 1
/// multiplier for pressure damage
var/pressuremod = 1
/// multiplier for EMP severity
var/emp_mod = 1
///Type of damage attack does
var/attack_type = BRUTE
///lowest possible punch damage. if this is set to 0, punches will always miss
var/punchdamagelow = 1
///highest possible punch damage
var/punchdamagehigh = 10
///chance for a punch to stun
var/punchstunchance = 0.1
///values of inaccuracy that adds to the spread of any ranged weapon
var/aiminginaccuracy = 0
///base electrocution coefficient
var/siemens_coeff = 1
///base action speed coefficient
var/action_speed_coefficient = 1
///what kind of damage overlays (if any) appear on our species when wounded?
var/damage_overlay_type = "human"
///to use MUTCOLOR with a fixed color that's independent of dna.feature["mcolor"]
var/fixed_mut_color = ""
///special mutation that can be found in the genepool. Dont leave empty or changing species will be a headache
var/inert_mutation = DWARFISM
///used to set the mobs deathsound on species change
var/deathsound
///Barefoot step sound
var/barefoot_step_sound = FOOTSTEP_MOB_BAREFOOT
///Sounds to override barefeet walking
var/list/special_step_sounds
///How loud to play the step override
var/special_step_volume = 50
///Sounds to play while walking regardless of wearing shoes
var/list/special_walk_sounds
///Special sound for grabbing
var/grab_sound
///yogs - audio of a species' scream
var/screamsound //yogs - grabs scream from screamsound list or string
var/husk_color = "#A6A6A6"
var/creampie_id = "creampie_human"
/// The visual effect of the attack.
var/attack_effect = ATTACK_EFFECT_PUNCH
///is a flying species, just a check for some things
var/flying_species = FALSE
///the actual flying ability given to flying species
var/datum/action/innate/flight/fly
///the icon used for the wings + details icon of a different source colour
var/wings_icon = "Angel"
var/wings_detail
/// What kind of gibs to spawn
var/species_gibs = "human"
/// Can this species use numbers in its name?
var/allow_numbers_in_name = FALSE
///Does this species have different sprites according to sex? A species may have sexes, but only one kind of bodypart sprite like chests
var/is_dimorphic = TRUE
/// species-only traits. Can be found in DNA.dm
var/list/species_traits = list()
/// generic traits tied to having the species
var/list/inherent_traits = list()
///biotypes, used for viruses and the like
var/list/inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID
/// punch-specific attack verbs
var/list/attack_verbs = list("punch")
///the melee attack sound
var/sound/attack_sound = 'sound/weapons/punch1.ogg'
///the swing and miss sound
var/sound/miss_sound = 'sound/weapons/punchmiss.ogg'
/// list of mobs that will ignore this species
var/list/mob/living/ignored_by = list()
//Breathing!
///what type of gas is breathed
var/breathid = GAS_O2
/// Special typing indicators
var/bubble_icon = BUBBLE_DEFAULT
/// The icon_state of the fire overlay added when sufficently ablaze and standing. see onfire.dmi
var/fire_overlay = "human" //not used until monkey is added as a species type rather than a mob
//Do NOT remove by setting to null. use OR make a RESPECTIVE TRAIT (removing stomach? add the NOSTOMACH trait to your species)
//why does it work this way? because traits also disable the downsides of not having an organ, removing organs but not having the trait will make your species die
///Replaces default brain with a different organ
var/obj/item/organ/brain/mutantbrain = /obj/item/organ/brain
///Replaces default heart with a different organ
var/obj/item/organ/heart/mutantheart = /obj/item/organ/heart
///Replaces default lungs with a different organ
var/obj/item/organ/lungs/mutantlungs = /obj/item/organ/lungs
///Replaces default eyes with a different organ
var/obj/item/organ/eyes/mutanteyes = /obj/item/organ/eyes
///Replaces default ears with a different organ
var/obj/item/organ/ears/mutantears = /obj/item/organ/ears
///Replaces default tongue with a different organ
var/obj/item/organ/tongue/mutanttongue = /obj/item/organ/tongue
///Replaces default liver with a different organ
var/obj/item/organ/liver/mutantliver = /obj/item/organ/liver
///Replaces default stomach with a different organ
var/obj/item/organ/stomach/mutantstomach = /obj/item/organ/stomach
///Replaces default appendix with a different organ.
var/obj/item/organ/appendix/mutantappendix = /obj/item/organ/appendix
///Forces a species tail
var/obj/item/organ/tail/mutanttail = /obj/item/organ/tail
///Forces an item into this species' hands. Only an honorary mutantthing because this is not an organ and not loaded in the same way, you've been warned to do your research.
var/obj/item/mutanthands
var/override_float = FALSE
///Bitflag that controls what in game ways can select this species as a spawnable source. Think magic mirror and pride mirror, slime extract, ERT etc, see defines in __DEFINES/mobs.dm, defaults to NONE, so people actually have to think about it
var/changesource_flags = NONE
//The component to add when swimming
var/swimming_component = /datum/component/swimming
var/smells_like = "something alien"
//Should we preload this species's organs?
var/preload = TRUE
//for preternis + synths
var/draining = FALSE
///Does our species have colors for its' damage overlays?
var/use_damage_color = TRUE
/// Do we try to prevent reset_perspective() from working? Useful for Dullahans to stop perspective changes when they're looking through their head.
var/prevent_perspective_change = FALSE
/// List of the type path of every ability innate to this species
var/list/species_abilities = list()
/// List of the created abilities, stored for the purpose of removal later, please do not touch this if you don't need to
var/list/datum/action/instantiated_abilities = list()
///////////
// PROCS //
///////////
/datum/species/New()
if(!limbs_id) //if we havent set a limbs id to use, just use our own id
limbs_id = id
if(!plural_form)
plural_form = "[name]\s"
return ..()
/// Gets a list of all species available to choose in roundstart.
/proc/get_selectable_species()
RETURN_TYPE(/list)
if (!GLOB.roundstart_races.len)
GLOB.roundstart_races = generate_selectable_species()
return GLOB.roundstart_races
/**
* Generates species available to choose in character setup at roundstart
*
* This proc generates which species are available to pick from in character setup.
* If there are no available roundstart species, defaults to human.
*/
/proc/generate_selectable_species()
var/list/selectable_species = list()
for(var/species_type in subtypesof(/datum/species))
var/datum/species/species = new species_type
if(species.check_roundstart_eligible())
selectable_species += species.id
qdel(species)
if(!selectable_species.len)
selectable_species += "human"
return selectable_species
/**
* Checks if a species is eligible to be picked at roundstart.
*
* Checks the config to see if this species is allowed to be picked in the character setup menu.
* Used by [/proc/generate_selectable_species].
*/
/datum/species/proc/check_roundstart_eligible()
if(id in (CONFIG_GET(keyed_list/roundstart_races)))
return TRUE
return FALSE
/datum/species/proc/check_mentor()
if(id in (CONFIG_GET(keyed_list/mentor_races)))
return TRUE
return FALSE
/datum/species/proc/random_name(gender,unique,lastname)
if(unique)
return random_unique_name(gender)
var/randname
if(gender == MALE)
randname = pick(GLOB.first_names_male)
else
randname = pick(GLOB.first_names_female)
if(lastname)
randname += " [lastname]"
else
randname += " [pick(GLOB.last_names)]"
return randname
//used to add things to the no_equip list that'll get inherited on species changes
/datum/species/proc/add_no_equip_slot(mob/living/carbon/C, slot, source)
if(!("[slot]" in extra_no_equip))
extra_no_equip["[slot]"] = list(source)
else
extra_no_equip["[slot]"] |= list(source)
var/obj/item/thing = C.get_item_by_slot(slot)
if(thing && (!thing.species_exception || !is_type_in_list(src,thing.species_exception)))
C.dropItemToGround(thing)
//removes something from the extra_no_equip list as well as the normal no_equip list
/datum/species/proc/remove_no_equip_slot(mob/living/carbon/C, slot, source)
extra_no_equip["[slot]"] -= source
if(!length(extra_no_equip["[slot]"]))
extra_no_equip -= "[slot]"
//Called when cloning, copies some vars that should be kept
/datum/species/proc/copy_properties_from(datum/species/old_species)
return
//Please override this locally if you want to define when what species qualifies for what rank if human authority is enforced.
/datum/species/proc/qualifies_for_rank(rank, list/features)
if(rank in GLOB.command_positions)
return 0
return 1
/datum/species/proc/has_toes()
return FALSE
/// Sprite to show for photocopying mob butts
/datum/species/proc/get_butt_sprite(mob/living/carbon/human/human)
return human.gender == FEMALE ? BUTT_SPRITE_HUMAN_FEMALE : BUTT_SPRITE_HUMAN_MALE
/**
* Corrects organs in a carbon, removing ones it doesn't need and adding ones it does.
*
* Takes all organ slots, removes organs a species should not have, adds organs a species should have.
* can use replace_current to refresh all organs, creating an entirely new set.
*
* Arguments:
* * C - carbon, the owner of the species datum AKA whoever we're regenerating organs in
* * old_species - datum, used when regenerate organs is called in a switching species to remove old mutant organs.
* * replace_current - boolean, forces all old organs to get deleted whether or not they pass the species' ability to keep that organ
* * visual_only - boolean, only load organs that change how the species looks. Do not use for normal gameplay stuff
*/
/datum/species/proc/regenerate_organs(mob/living/carbon/C, datum/species/old_species, replace_current = TRUE, visual_only = FALSE)
//what should be put in if there is no mutantorgan (brains handled separately)
var/list/slot_mutantorgans = list(ORGAN_SLOT_BRAIN = mutantbrain, ORGAN_SLOT_HEART = mutantheart, ORGAN_SLOT_LUNGS = mutantlungs, ORGAN_SLOT_APPENDIX = mutantappendix, \
ORGAN_SLOT_EYES = mutanteyes, ORGAN_SLOT_EARS = mutantears, ORGAN_SLOT_TONGUE = mutanttongue, ORGAN_SLOT_LIVER = mutantliver, ORGAN_SLOT_STOMACH = mutantstomach, ORGAN_SLOT_TAIL = mutanttail)
for(var/slot in list(ORGAN_SLOT_BRAIN, ORGAN_SLOT_HEART, ORGAN_SLOT_LUNGS, ORGAN_SLOT_APPENDIX, \
ORGAN_SLOT_EYES, ORGAN_SLOT_EARS, ORGAN_SLOT_TONGUE, ORGAN_SLOT_LIVER, ORGAN_SLOT_STOMACH, ORGAN_SLOT_TAIL))
var/obj/item/organ/oldorgan = C.getorganslot(slot) //used in removing
var/obj/item/organ/neworgan = slot_mutantorgans[slot] //used in adding
if(visual_only && !initial(neworgan.visual))
continue
var/used_neworgan = FALSE
neworgan = SSwardrobe.provide_type(neworgan)
var/should_have = neworgan.get_availability(src) //organ proc that points back to a species trait (so if the species is supposed to have this organ)
if(oldorgan && (!should_have || replace_current))
if(slot == ORGAN_SLOT_BRAIN)
var/obj/item/organ/brain/brain = oldorgan
if(!brain.decoy_override)//"Just keep it if it's fake" - confucius, probably
brain.Remove(C, TRUE, TRUE) //brain argument used so it doesn't cause any... sudden death.
QDEL_NULL(brain)
oldorgan = null //now deleted
else
oldorgan.Remove(C,TRUE)
QDEL_NULL(oldorgan) //we cannot just tab this out because we need to skip the deleting if it is a decoy brain.
if(oldorgan)
oldorgan.setOrganDamage(0)
else if(should_have)
if(slot == ORGAN_SLOT_TAIL)
// Special snowflake code to handle tail appearances
var/obj/item/organ/tail/new_tail = neworgan
if(iscatperson(C))
new_tail.tail_type = C.dna.features["tail_human"]
if(ispolysmorph(C))
new_tail.tail_type = C.dna.features["tail_polysmorph"]
if(islizard(C))
var/obj/item/organ/tail/lizard/new_lizard_tail = neworgan
new_lizard_tail.tail_type = C.dna.features["tail_lizard"]
new_lizard_tail.spines = C.dna.features["spines"]
if(isvox(C))
var/obj/item/organ/tail/vox/new_vox_tail = neworgan
new_vox_tail.tail_type = C.dna.features["vox_skin_tone"]
new_vox_tail.tail_markings = C.dna.features["vox_tail_markings"]
// if(tail && (!should_have_tail || replace_current))
// tail.Remove(C,1)
// QDEL_NULL(tail)
// if(should_have_tail && !tail)
// tail = new mutanttail
// if(iscatperson(C))
// tail.tail_type = C.dna.features["tail_human"]
// if(ispolysmorph(C))
// tail.tail_type = C.dna.features["tail_polysmorph"]
// if(islizard(C))
// var/obj/item/organ/tail/lizard/T = tail
// T.tail_type = C.dna.features["tail_lizard"]
// T.spines = C.dna.features["spines"]
// tail.Insert(C)
used_neworgan = TRUE
neworgan.Insert(C, TRUE, FALSE)
if(!used_neworgan)
qdel(neworgan)
if(old_species)
for(var/mutantorgan in old_species.mutant_organs)
// Snowflake check. If our species share this mutant organ, let's not remove it
// just yet as we'll be properly replacing it later.
if(mutantorgan in mutant_organs)
continue
var/obj/item/organ/I = C.getorgan(mutantorgan)
if(I)
I.Remove(C)
QDEL_NULL(I)
for(var/organ_path in mutant_organs)
var/obj/item/organ/current_organ = C.getorgan(organ_path)
if(!current_organ || replace_current)
var/obj/item/organ/replacement = SSwardrobe.provide_type(organ_path)
// If there's an existing mutant organ, we're technically replacing it.
// Let's abuse the snowflake proc that skillchips added. Basically retains
// feature parity with every other organ too.
//if(current_organ)
// current_organ.before_organ_replacement(replacement)
// organ.Insert will qdel any current organs in that slot, so we don't need to.
replacement.Insert(C, TRUE, FALSE)
/datum/species/proc/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load)
// Change the gender to fit with the new species
if(!possible_genders || possible_genders.len < 1)
stack_trace("[type] has no possible genders!")
C.gender = PLURAL // uh oh
else if(possible_genders.len == 1)
C.gender = possible_genders[1] // some species only have one gender
else if(!(C.gender in possible_genders))
C.gender = pick(possible_genders) // randomized gender
// Drop the items the new species can't wear
extra_no_equip = old_species.extra_no_equip.Copy()
for(var/slot_id in no_equip)
var/obj/item/thing = C.get_item_by_slot(slot_id)
if(thing && (!thing.species_exception || !is_type_in_list(src,thing.species_exception)))
C.dropItemToGround(thing)
if(C.hud_used)
C.hud_used.update_locked_slots()
// this needs to be FIRST because qdel calls update_body which checks if we have DIGITIGRADE legs or not and if not then removes DIGITIGRADE from species_traits
if((DIGITIGRADE in species_traits) && !(DIGITIGRADE in old_species.species_traits))
C.digitigrade_leg_swap(FALSE)
C.mob_biotypes = inherent_biotypes
C.bubble_icon = bubble_icon
regenerate_organs(C,old_species)
if(exotic_bloodtype && C.dna.blood_type != exotic_bloodtype)
C.dna.blood_type = get_blood_type(exotic_bloodtype)
if(old_species.mutanthands)
for(var/obj/item/I in C.held_items)
if(istype(I, old_species.mutanthands))
qdel(I)
if(mutanthands)
// Drop items in hands
// If you're lucky enough to have a TRAIT_NODROP item, then it stays.
for(var/V in C.held_items)
var/obj/item/I = V
if(istype(I))
C.dropItemToGround(I)
else //Entries in the list should only ever be items or null, so if it's not an item, we can assume it's an empty hand
C.put_in_hands(new mutanthands())
if(inherent_biotypes & MOB_ROBOTIC)
for(var/obj/item/bodypart/B in C.bodyparts)
B.change_bodypart_status(BODYPART_ROBOTIC) // Makes all Bodyparts robotic.
B.render_like_organic = TRUE
if(NOMOUTH in species_traits)
for(var/obj/item/bodypart/head/head in C.bodyparts)
head.mouth = FALSE
for(var/X in inherent_traits)
ADD_TRAIT(C, X, SPECIES_TRAIT)
if(TRAIT_VIRUSIMMUNE in inherent_traits)
for(var/datum/disease/A in C.diseases)
A.cure(FALSE)
if(flying_species && isnull(fly))
fly = new
fly.Grant(C)
for(var/ability_path in species_abilities)
var/datum/action/ability = new ability_path(C)
ability.Grant(C)
instantiated_abilities += ability
C.add_movespeed_modifier(MOVESPEED_ID_SPECIES, TRUE, 100, override=TRUE, multiplicative_slowdown=speedmod, movetypes=(~FLYING))
C.regenerate_icons()
SEND_SIGNAL(C, COMSIG_SPECIES_GAIN, src, old_species)
if(!(C.voice_type?.can_use(id)))
C.voice_type = get_random_valid_voice(id)
/datum/species/proc/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load)
if(C.dna.species.exotic_bloodtype)
C.dna.blood_type = random_blood_type()
if((DIGITIGRADE in species_traits) && !(DIGITIGRADE in new_species.species_traits))
C.digitigrade_leg_swap(TRUE)
if(inherent_biotypes & MOB_ROBOTIC)
for(var/obj/item/bodypart/B in C.bodyparts)
B.change_bodypart_status(BODYPART_ORGANIC, FALSE, TRUE)
B.render_like_organic = FALSE
if(NOMOUTH in species_traits)
for(var/obj/item/bodypart/head/head in C.bodyparts)
head.mouth = TRUE
for(var/X in inherent_traits)
REMOVE_TRAIT(C, X, SPECIES_TRAIT)
//If their inert mutation is not the same, swap it out
if((inert_mutation != new_species.inert_mutation) && LAZYLEN(C.dna.mutation_index) && (inert_mutation in C.dna.mutation_index))
C.dna.remove_mutation(inert_mutation)
//keep it at the right spot, so we can't have people taking shortcuts
var/location = C.dna.mutation_index.Find(inert_mutation)
C.dna.mutation_index[location] = new_species.inert_mutation
C.dna.default_mutation_genes[location] = C.dna.mutation_index[location]
C.dna.mutation_index[new_species.inert_mutation] = create_sequence(new_species.inert_mutation)
C.dna.default_mutation_genes[new_species.inert_mutation] = C.dna.mutation_index[new_species.inert_mutation]
if(flying_species)
fly.Remove(C)
QDEL_NULL(fly)
if(C.movement_type & FLYING)
ToggleFlight(C)
if(C?.dna?.species && (C.dna.features["wings"] == wings_icon))
if("wings" in C.dna.species.mutant_bodyparts)
C.dna.species.mutant_bodyparts -= "wings"
C.dna.features["wings"] = "None"
if(wings_detail && C.dna.features["wingsdetail"] == wings_detail)
if("wingsdetail" in C.dna.species.mutant_bodyparts)
C.dna.species.mutant_bodyparts -= "wingsdetail"
C.dna.features["wingsdetail"] = "None"
C.update_body()
for(var/datum/action/ability as anything in instantiated_abilities)
ability.Remove(C)
instantiated_abilities -= ability
qdel(ability)
C.remove_movespeed_modifier(MOVESPEED_ID_SPECIES)
SEND_SIGNAL(C, COMSIG_SPECIES_LOSS, src)
/datum/species/proc/handle_hair(mob/living/carbon/human/H, forced_colour)
H.remove_overlay(HAIR_LAYER)
var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD)
if(!HD) //Decapitated
return
if(HAS_TRAIT(H, TRAIT_HUSK))
return
var/datum/sprite_accessory/S
var/list/standing = list()
var/hair_hidden = FALSE //ignored if the matching dynamic_X_suffix is non-empty
var/facialhair_hidden = FALSE // ^
var/dynamic_hair_suffix = "" //if this is non-null, and hair+suffix matches an iconstate, then we render that hair instead
var/dynamic_fhair_suffix = ""
//for augmented heads
if(HD.status == BODYPART_ROBOTIC && !yogs_draw_robot_hair) //yogs - allow for robot head hair
return
//we check if our hat or helmet hides our facial hair.
if(H.head)
var/obj/item/I = H.head
if(istype(I, /obj/item/clothing))
var/obj/item/clothing/C = I
dynamic_fhair_suffix = C.dynamic_fhair_suffix
if(I.flags_inv & HIDEFACIALHAIR)
facialhair_hidden = TRUE
if(H.wear_mask)
var/obj/item/clothing/mask/M = H.wear_mask
dynamic_fhair_suffix = M.dynamic_fhair_suffix //mask > head in terms of facial hair
if(M.flags_inv & HIDEFACIALHAIR)
facialhair_hidden = TRUE
if(("vox_facial_quills" in H.dna.species.mutant_bodyparts) && !facialhair_hidden)
S = GLOB.vox_facial_quills_list[H.dna.features["vox_facial_quills"]]
if(S)
var/mutable_appearance/facial_quills_overlay = mutable_appearance(layer = -HAIR_LAYER, appearance_flags = KEEP_TOGETHER)
var/mutable_appearance/facial_quills_base = mutable_appearance(S.icon, S.icon_state)
facial_quills_base.color = forced_colour || H.facial_hair_color
if(S.color_blend_mode == COLOR_BLEND_ADD)
facial_quills_base.color = COLOR_MATRIX_ADD(facial_quills_base.color)
facial_quills_overlay.overlays += facial_quills_base
facial_quills_overlay.alpha = hair_alpha
standing += facial_quills_overlay
if(H.facial_hair_style && (FACEHAIR in species_traits) && (!facialhair_hidden || dynamic_fhair_suffix))
S = GLOB.facial_hair_styles_list[H.facial_hair_style]
if(S)
//List of all valid dynamic_fhair_suffixes
var/static/list/fextensions
if(!fextensions)
var/icon/fhair_extensions = icon('icons/mob/facialhair_extensions.dmi')
fextensions = list()
for(var/s in fhair_extensions.IconStates(1))
fextensions[s] = TRUE
qdel(fhair_extensions)
//Is hair+dynamic_fhair_suffix a valid iconstate?
var/fhair_state = S.icon_state
var/fhair_file = S.icon
if(fextensions[fhair_state+dynamic_fhair_suffix])
fhair_state += dynamic_fhair_suffix
fhair_file = 'icons/mob/facialhair_extensions.dmi'
var/mutable_appearance/facial_overlay = mutable_appearance(fhair_file, fhair_state, -HAIR_LAYER)
if(!forced_colour)
if(hair_color)
if(hair_color == "mutcolor")
facial_overlay.color = H.dna.features["mcolor"]
else if(hair_color == "fixedmutcolor")
facial_overlay.color = fixed_mut_color
else
facial_overlay.color = hair_color
else
facial_overlay.color = H.facial_hair_color
else
facial_overlay.color = forced_colour
facial_overlay.alpha = hair_alpha
standing += facial_overlay
if(H.head)
var/obj/item/I = H.head
if(istype(I, /obj/item/clothing))
var/obj/item/clothing/C = I
dynamic_hair_suffix = C.dynamic_hair_suffix
if(I.flags_inv & HIDEHAIR)
hair_hidden = TRUE
if(H.wear_mask)
var/obj/item/clothing/mask/M = H.wear_mask
if(!dynamic_hair_suffix) //head > mask in terms of head hair
dynamic_hair_suffix = M.dynamic_hair_suffix
if(M.flags_inv & HIDEHAIR)
hair_hidden = TRUE
if(!hair_hidden || dynamic_hair_suffix)
var/mutable_appearance/hair_overlay = mutable_appearance(layer = -HAIR_LAYER)
var/mutable_appearance/gradient_overlay = mutable_appearance(layer = -HAIR_LAYER)
if(!hair_hidden && !H.getorgan(/obj/item/organ/brain)) //Applies the debrained overlay if there is no brain
if(!(NOBLOOD in species_traits))
hair_overlay.icon = 'icons/mob/human_face.dmi'
hair_overlay.icon_state = "debrained"
else if(H.hair_style && (HAIR in species_traits))
S = GLOB.hair_styles_list[H.hair_style]
if(S)
//List of all valid dynamic_hair_suffixes
var/static/list/extensions
if(!extensions)
var/icon/hair_extensions = icon('icons/mob/hair_extensions.dmi') //hehe
extensions = list()
for(var/s in hair_extensions.IconStates(1))
extensions[s] = TRUE
qdel(hair_extensions)
//Is hair+dynamic_hair_suffix a valid iconstate?
var/hair_state = S.icon_state
var/hair_file = S.icon
if(extensions[hair_state+dynamic_hair_suffix])
hair_state += dynamic_hair_suffix
hair_file = 'icons/mob/hair_extensions.dmi'
hair_overlay.icon = hair_file
hair_overlay.icon_state = hair_state
if(!forced_colour)
if(hair_color)
if(hair_color == "mutcolor")
hair_overlay.color = H.dna.features["mcolor"]
else if(hair_color == "fixedmutcolor")
hair_overlay.color = fixed_mut_color
else
hair_overlay.color = hair_color
else
hair_overlay.color = H.hair_color
//Gradients
grad_style = H.grad_style
grad_color = H.grad_color
if(grad_style)
var/datum/sprite_accessory/gradient = GLOB.hair_gradients_list[grad_style]
var/icon/temp = icon(gradient.icon, gradient.icon_state)
var/icon/temp_hair = icon(hair_file, hair_state)
temp.Blend(temp_hair, ICON_ADD)
gradient_overlay.icon = temp
gradient_overlay.color = grad_color
else
hair_overlay.color = forced_colour
hair_overlay.alpha = hair_alpha
if(OFFSET_FACE in H.dna.species.offset_features)
hair_overlay.pixel_x += H.dna.species.offset_features[OFFSET_FACE][1]
hair_overlay.pixel_y += H.dna.species.offset_features[OFFSET_FACE][2]
if(hair_overlay.icon)
standing += hair_overlay
standing += gradient_overlay
if("pod_hair" in H.dna.species.mutant_bodyparts)
//alright bear with me for a second while i explain this awful code since it was literally 3 days of me bumbling through blind
//for hair code to work, you need to start by removing the layer, as in the beginning with remove_overlay(head), then you need to use a mutable appearance variable
//the mutable appearance will store the sprite file dmi, the name of the sprite (icon_state), and the layer this will go on (in this case HAIR_LAYER)
//those are the basic variables, then you color the sprite with whatever source color you're using and set the alpha. from there it's added to the "standing" list
//which is storing all the individual mutable_appearance variables (each one is a sprite), and then standing is loaded into the H.overlays_standing and finalized
//with apply_overlays.
//if you're working with sprite code i hope this helps because i wish i was dead now.
S = GLOB.pod_hair_list[H.dna.features["pod_hair"]]
if(S)
if(ReadHSV(RGBtoHSV(H.hair_color))[3] <= ReadHSV("#777777")[3])
H.hair_color = H.dna.species.default_color
var/hair_state = S.icon_state
var/hair_file = S.icon
hair_overlay.icon = hair_file
hair_overlay.icon_state = hair_state
if(!forced_colour)
if(hair_color)
if(hair_color == "mutcolor")
hair_overlay.color = H.dna.features["mcolor"]
else if(hair_color == "fixedmutcolor")
hair_overlay.color = fixed_mut_color
else
hair_overlay.color = hair_color
else
hair_overlay.color = H.hair_color
hair_overlay.alpha = hair_alpha
standing+=hair_overlay
//var/mutable_appearance/pod_flower = mutable_appearance(GLOB.pod_flower_list[H.dna.features["pod_flower"]].icon, GLOB.pod_flower_list[H.dna.features["pod_flower"]].icon_state, -HAIR_LAYER)
S = GLOB.pod_flower_list[H.dna.features["pod_flower"]]
if(S)
var/flower_state = S.icon_state
var/flower_file = S.icon
// flower_overlay.icon = flower_file
// flower_overlay.icon_state = flower_state
var/mutable_appearance/flower_overlay = mutable_appearance(flower_file, flower_state, -HAIR_LAYER)
if(!forced_colour)
if(hair_color)
if(hair_color == "mutcolor")
flower_overlay.color = H.dna.features["mcolor"]
else if(hair_color == "fixedmutcolor")
flower_overlay.color = fixed_mut_color
else
flower_overlay.color = hair_color
else
flower_overlay.color = H.facial_hair_color
flower_overlay.alpha = hair_alpha
standing += flower_overlay
if(("vox_quills" in H.dna.species.mutant_bodyparts) && !hair_hidden)
S = GLOB.vox_quills_list[H.dna.features["vox_quills"]]
if(S)
var/mutable_appearance/quills_overlay = mutable_appearance(layer = -HAIR_LAYER, appearance_flags = KEEP_TOGETHER)
var/mutable_appearance/quills_base = mutable_appearance(S.icon, S.icon_state)
quills_base.color = forced_colour || H.hair_color
if(S.color_blend_mode == COLOR_BLEND_ADD)
quills_base.color = COLOR_MATRIX_ADD(quills_base.color)
quills_overlay.overlays += quills_base
//Gradients
grad_style = H.grad_style
grad_color = H.grad_color
if(grad_style)
var/datum/sprite_accessory/gradient = GLOB.hair_gradients_list[grad_style]
var/mutable_appearance/gradient_quills = mutable_appearance(gradient.icon, gradient.icon_state)
gradient_quills.color = COLOR_MATRIX_OVERLAY(grad_color)
gradient_quills.blend_mode = BLEND_INSET_OVERLAY
quills_overlay.overlays += gradient_quills
quills_overlay.alpha = hair_alpha
standing += quills_overlay
if(standing.len)
H.overlays_standing[HAIR_LAYER] = standing
H.apply_overlay(HAIR_LAYER)
/datum/species/proc/handle_body(mob/living/carbon/human/H)
H.remove_overlay(BODY_LAYER)
var/list/standing = list()
var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD)
if(HD && !(HAS_TRAIT(H, TRAIT_HUSK)))
// lipstick
if(H.lip_style && (LIPS in species_traits))
var/mutable_appearance/lip_overlay = mutable_appearance('icons/mob/human_face.dmi', "lips_[H.lip_style]", -BODY_LAYER)
lip_overlay.color = H.lip_color
if(OFFSET_FACE in H.dna.species.offset_features)
lip_overlay.pixel_x += H.dna.species.offset_features[OFFSET_FACE][1]
lip_overlay.pixel_y += H.dna.species.offset_features[OFFSET_FACE][2]
standing += lip_overlay
#define OFFSET_X 1
#define OFFSET_Y 2
// eyes
if(!(NOEYESPRITES in species_traits))
var/obj/item/organ/eyes/parent_eyes = H.getorganslot(ORGAN_SLOT_EYES)
var/mutable_appearance/eye_overlay
if(parent_eyes)
eye_overlay += parent_eyes.generate_body_overlay(H)
else
var/mutable_appearance/missing_eyes = mutable_appearance(HD.eyes_icon, "eyes_missing", -BODY_LAYER)
if(OFFSET_FACE in offset_features)
missing_eyes.pixel_x += offset_features[OFFSET_FACE][1]
missing_eyes.pixel_y += offset_features[OFFSET_FACE][2]
eye_overlay += missing_eyes
standing += eye_overlay
#undef OFFSET_X
#undef OFFSET_Y
//Underwear, Undershirts & Socks
if(!(NO_UNDERWEAR in species_traits))
if(H.underwear)
var/datum/sprite_accessory/underwear/underwear = GLOB.underwear_list[H.underwear]
if(underwear)
if(HAS_TRAIT(H, TRAIT_SKINNY))
standing += wear_skinny_version(underwear.icon_state, underwear.icon, BODY_LAYER) //Neat, this works
else if((H.gender == FEMALE && (FEMALE in possible_genders)) && H.dna.species.is_dimorphic)
standing += wear_female_version(underwear.icon_state, underwear.icon, BODY_LAYER, flat = !!(H.mob_biotypes & MOB_REPTILE)) // lizards
else
var/mutable_appearance/underwear_overlay = mutable_appearance(underwear.icon, underwear.icon_state, -BODY_LAYER)
if(H.dna.species.id in underwear.sprite_sheets)
if(icon_exists(underwear.sprite_sheets[H.dna.species.id], underwear.icon_state))
underwear_overlay.icon = underwear.sprite_sheets[H.dna.species.id]
standing += underwear_overlay
if(H.undershirt)
var/datum/sprite_accessory/undershirt/undershirt = GLOB.undershirt_list[H.undershirt]
if(undershirt)
if(HAS_TRAIT(H, TRAIT_SKINNY)) //Check for skinny first
standing += wear_skinny_version(undershirt.icon_state, undershirt.icon, BODY_LAYER)
else if((H.gender == FEMALE && (FEMALE in possible_genders)) && H.dna.species.is_dimorphic)
standing += wear_female_version(undershirt.icon_state, undershirt.icon, BODY_LAYER, flat = !!(H.mob_biotypes & MOB_REPTILE)) // lizards
else
var/mutable_appearance/undershirt_overlay = mutable_appearance(undershirt.icon, undershirt.icon_state, -BODY_LAYER)
if(H.dna.species.id in undershirt.sprite_sheets)
if(icon_exists(undershirt.sprite_sheets[H.dna.species.id], undershirt.icon_state))
undershirt_overlay.icon = undershirt.sprite_sheets[H.dna.species.id]
standing += undershirt_overlay
if(H.socks && H.get_num_legs(FALSE) >= 2 && !(DIGITIGRADE in species_traits))
var/datum/sprite_accessory/socks/socks = GLOB.socks_list[H.socks]
if(socks)
var/mutable_appearance/socks_overlay = mutable_appearance(socks.icon, socks.icon_state, -BODY_LAYER)
if(H.dna.species.id in socks.sprite_sheets)
if(icon_exists(socks.sprite_sheets[H.dna.species.id], socks.icon_state))
socks_overlay.icon = socks.sprite_sheets[H.dna.species.id]
standing += socks_overlay
if(standing.len)
H.overlays_standing[BODY_LAYER] = standing
H.apply_overlay(BODY_LAYER)
handle_mutant_bodyparts(H)
/datum/species/proc/handle_mutant_bodyparts(mob/living/carbon/human/H, forced_colour)
var/list/bodyparts_to_add = mutant_bodyparts.Copy()
var/list/relevent_layers = list(BODY_BEHIND_LAYER, BODY_ADJ_LAYER, BODY_FRONT_LAYER)
var/list/standing = list()
H.remove_overlay(BODY_BEHIND_LAYER)
H.remove_overlay(BODY_ADJ_LAYER)
H.remove_overlay(BODY_FRONT_LAYER)
if(!mutant_bodyparts)
return
var/obj/item/bodypart/head/HD = H.get_bodypart(BODY_ZONE_HEAD)
if("tail_lizard" in mutant_bodyparts)
if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
bodyparts_to_add -= "tail_lizard"
if("waggingtail_lizard" in mutant_bodyparts)
if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
bodyparts_to_add -= "waggingtail_lizard"
else if ("tail_lizard" in mutant_bodyparts)
bodyparts_to_add -= "waggingtail_lizard"
if("tail_human" in mutant_bodyparts)
if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
bodyparts_to_add -= "tail_human"
if("waggingtail_human" in mutant_bodyparts)
if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
bodyparts_to_add -= "waggingtail_human"
else if ("tail_human" in mutant_bodyparts)
bodyparts_to_add -= "waggingtail_human"
if("tail_polysmorph" in mutant_bodyparts)
if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
bodyparts_to_add -= "tail_polysmorph"
if("spines" in mutant_bodyparts)
if(!H.dna.features["spines"] || H.dna.features["spines"] == "None" || H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
bodyparts_to_add -= "spines"
if("waggingspines" in mutant_bodyparts)
if(!H.dna.features["spines"] || H.dna.features["spines"] == "None" || H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
bodyparts_to_add -= "waggingspines"
else if ("tail" in mutant_bodyparts)
bodyparts_to_add -= "waggingspines"
if("snout" in mutant_bodyparts) //Take a closer look at that snout!
if((H.wear_mask && !(H.wear_mask.mutantrace_variation & DIGITIGRADE_VARIATION) && (H.wear_mask.flags_inv & HIDEFACE)) || (H.head && (H.head.flags_inv & HIDEFACE)) || !HD || HD.status == BODYPART_ROBOTIC)
bodyparts_to_add -= "snout"
if("frills" in mutant_bodyparts)
if(!H.dna.features["frills"] || H.dna.features["frills"] == "None" || H.head && (H.head.flags_inv & HIDEEARS) || !HD || HD.status == BODYPART_ROBOTIC)
bodyparts_to_add -= "frills"
if("horns" in mutant_bodyparts)
if(!H.dna.features["horns"] || H.dna.features["horns"] == "None" || H.head && (H.head.flags_inv & HIDEHAIR) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || !HD || HD.status == BODYPART_ROBOTIC)
bodyparts_to_add -= "horns"
if("ears" in mutant_bodyparts)
if(!H.dna.features["ears"] || H.dna.features["ears"] == "None" || H.head && (H.head.flags_inv & HIDEHAIR) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || !HD || HD.status == BODYPART_ROBOTIC)
bodyparts_to_add -= "ears"
if("wings" in mutant_bodyparts)
if(!H.dna.features["wings"] || H.dna.features["wings"] == "None" || (H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT) && (!H.wear_suit.species_exception || !is_type_in_list(src, H.wear_suit.species_exception))))
bodyparts_to_add -= "wings"
if("wings_open" in mutant_bodyparts)
if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT) && (!H.wear_suit.species_exception || !is_type_in_list(src, H.wear_suit.species_exception)))
bodyparts_to_add -= "wings_open"
else if ("wings" in mutant_bodyparts)
bodyparts_to_add -= "wings_open"
if("wingsdetail" in mutant_bodyparts)
if(!H.dna.features["wingsdetail"] || H.dna.features["wingsdetail"] == "None" || (H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT) && (!H.wear_suit.species_exception || !is_type_in_list(src, H.wear_suit.species_exception))))
bodyparts_to_add -= "wingsdetail"
if("wingsdetail_open" in mutant_bodyparts)
if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT) && (!H.wear_suit.species_exception || !is_type_in_list(src, H.wear_suit.species_exception)))
bodyparts_to_add -= "wingsdetail_open"
else if ("wingsdetail" in mutant_bodyparts)
bodyparts_to_add -= "wingsdetail_open"
if("ipc_screen" in mutant_bodyparts)
if(!H.dna.features["ipc_screen"] || H.dna.features["ipc_screen"] == "None" || (H.wear_mask && (H.wear_mask.flags_inv & HIDEEYES)) || !HD)
bodyparts_to_add -= "ipc_screen"
if("ipc_antenna" in mutant_bodyparts)
if(!H.dna.features["ipc_antenna"] || H.dna.features["ipc_antenna"] == "None" || H.head && (H.head.flags_inv & HIDEHAIR) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || !HD)
bodyparts_to_add -= "ipc_antenna"
if("teeth" in mutant_bodyparts)
if((H.wear_mask && (H.wear_mask.flags_inv & HIDEFACE)) || (H.head && (H.head.flags_inv & HIDEFACE)) || !HD || HD.status == BODYPART_ROBOTIC)
bodyparts_to_add -= "teeth"
if("dome" in mutant_bodyparts)
if(!H.dna.features["dome"] || H.dna.features["dome"] == "None" || H.head && (H.head.flags_inv & HIDEHAIR) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || !HD || HD.status == BODYPART_ROBOTIC)
bodyparts_to_add -= "dome"
if("ethereal_mark" in mutant_bodyparts)
if((H.wear_mask && (H.wear_mask.flags_inv & HIDEEYES)) || (H.head && (H.head.flags_inv & HIDEEYES)) || !HD || HD.status == BODYPART_ROBOTIC)
bodyparts_to_add -= "ethereal_mark"
if("preternis_antenna" in mutant_bodyparts)
if(H.head && (H.head.flags_inv & HIDEHAIR) || (H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || !HD)
bodyparts_to_add -= "preternis_antenna"
if("preternis_eye" in mutant_bodyparts)
if((H.wear_mask && (H.wear_mask.flags_inv & HIDEEYES)) || (H.head && (H.head.flags_inv & HIDEEYES)) || !HD)