mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-11 18:22:14 +00:00
Add Proteans to the game. (#3449)
## About The Pull Request Adds the funny nanite blob dudes from Vigro code into the game. It's more of a port of the idea since they're coded from the ground up. They will be more of a utility focused species then a combat focused species. **They are fragile.** - Eats metal. You need metal to live and you need metal to heal. - Healed by materials. You feed Proteans metal to heal them. - Difficult to kill, but extremely fragile. They are considered a deathless species but they are stuck in their suit if they die. **You have to order a new refactory from cargo to revive them** **Order Refactory > Screwdriver suit > Insert refactory > Wait 5 minutes.** - Easily dismembered. They have 30 seconds to recover their limbs and pop them on or they will melt into nothingness. Can do a lengthy heal which replaces missing limbs and easy to replace organs. - Without a refactory, you will wither away. - Without an orchastrator, you will have a lot of issues moving. - You can lock your suit on someone. (OOC escape will work) - You can assimilate modsuits. - [x] OOC escape - [x] Suit Transformation fixes - [x] Modsuit Assimilation - [x] Ensure organs are working - [x] Species info and lore - [x] Antag Proteans - [x] Custom damage. Disable various surgeries. - [x] Testing, polish, feedback. - [x] Runtime and CI fixing. - [ ] Live testing and balance. ## Why It's Good For The Game This was the second to top vote on species people wanted to see added and this is more custom mechanics then what is just a human reskin. ## Proof Of Testing   ## Changelog 🆑 StrangeWeirdKitten, Majkl-J add: New species: Proteans /🆑 --------- Co-authored-by: Waterpig <49160555+Majkl-J@users.noreply.github.com> Co-authored-by: Bubberbot <151680451+Bubberbot@users.noreply.github.com> Co-authored-by: Arturlang <24881678+Arturlang@users.noreply.github.com> Co-authored-by: aKromatopzia <94389683+aKromatopzia@users.noreply.github.com> Co-authored-by: Jinshee <96621959+Jinshee@users.noreply.github.com> Co-authored-by: Jinshee <manastra2536@gmail.com> Co-authored-by: JustMeTheIInd <145101584+JustMeTheIInd@users.noreply.github.com> Co-authored-by: nevimer <77420409+nevimer@users.noreply.github.com> Co-authored-by: Odairu <39929315+Odairu@users.noreply.github.com> Co-authored-by: LT3 <83487515+lessthnthree@users.noreply.github.com> Co-authored-by: Roxy <75404941+TealSeer@users.noreply.github.com>
This commit is contained in:
@@ -131,6 +131,9 @@
|
|||||||
//This limb is shadowy and will regen if shadowheal is active
|
//This limb is shadowy and will regen if shadowheal is active
|
||||||
#define BODYTYPE_SHADOW (1<<7)
|
#define BODYTYPE_SHADOW (1<<7)
|
||||||
// SKYRAT EDIT ADDITION
|
// SKYRAT EDIT ADDITION
|
||||||
|
|
||||||
|
/// Nanomachine bodypart
|
||||||
|
#define BODYTYPE_NANO (1<<8)
|
||||||
///The limb fits a modular custom shape
|
///The limb fits a modular custom shape
|
||||||
#define BODYSHAPE_CUSTOM (1<<9)
|
#define BODYSHAPE_CUSTOM (1<<9)
|
||||||
///The limb fits a taur body
|
///The limb fits a taur body
|
||||||
@@ -139,6 +142,7 @@
|
|||||||
#define BODYSHAPE_HIDE_SHOES (1<<11)
|
#define BODYSHAPE_HIDE_SHOES (1<<11)
|
||||||
///The limb causes glasses and hats to be drawn on layers 5 and 4 respectively. Currently used for snouts with the (Top) suffix, which are drawn on layer 6 and would normally cover facewear
|
///The limb causes glasses and hats to be drawn on layers 5 and 4 respectively. Currently used for snouts with the (Top) suffix, which are drawn on layer 6 and would normally cover facewear
|
||||||
#define BODYSHAPE_ALT_FACEWEAR_LAYER (1<<12)
|
#define BODYSHAPE_ALT_FACEWEAR_LAYER (1<<12)
|
||||||
|
|
||||||
// SKYRAT EDIT END
|
// SKYRAT EDIT END
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,8 @@
|
|||||||
#define ORGAN_EXTERNAL (1<<13)
|
#define ORGAN_EXTERNAL (1<<13)
|
||||||
/// This is a mutant organ, having this makes you a -derived mutant to health analyzers.
|
/// This is a mutant organ, having this makes you a -derived mutant to health analyzers.
|
||||||
#define ORGAN_MUTANT (1<<14)
|
#define ORGAN_MUTANT (1<<14)
|
||||||
|
/// BUBBER EDIT ADD - Nanoswarm organ
|
||||||
|
#define ORGAN_NANOMACHINE (1<<15)
|
||||||
|
|
||||||
/// Scarring on the right eye
|
/// Scarring on the right eye
|
||||||
#define RIGHT_EYE_SCAR (1<<0)
|
#define RIGHT_EYE_SCAR (1<<0)
|
||||||
@@ -44,6 +46,8 @@
|
|||||||
#define IS_ORGANIC_LIMB(limb) (limb.bodytype & BODYTYPE_ORGANIC)
|
#define IS_ORGANIC_LIMB(limb) (limb.bodytype & BODYTYPE_ORGANIC)
|
||||||
/// Helper to figure out if a limb is robotic
|
/// Helper to figure out if a limb is robotic
|
||||||
#define IS_ROBOTIC_LIMB(limb) (limb.bodytype & BODYTYPE_ROBOTIC)
|
#define IS_ROBOTIC_LIMB(limb) (limb.bodytype & BODYTYPE_ROBOTIC)
|
||||||
|
/// Helper to figure out if a limb is a nanomachine limb. --- BUBBER EDIT
|
||||||
|
#define IS_NANO_LIMB(limb) (limb.bodytype & BODYTYPE_NANO) // BUBBER EDIT END
|
||||||
/// Helper to figure out if a limb is a peg limb
|
/// Helper to figure out if a limb is a peg limb
|
||||||
#define IS_PEG_LIMB(limb) (limb.bodytype & BODYTYPE_PEG)
|
#define IS_PEG_LIMB(limb) (limb.bodytype & BODYTYPE_PEG)
|
||||||
|
|
||||||
|
|||||||
1
code/__DEFINES/~~bubber_defines/dna.dm
Normal file
1
code/__DEFINES/~~bubber_defines/dna.dm
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#define SPECIES_PROTEAN "protean"
|
||||||
@@ -12,3 +12,7 @@
|
|||||||
#define COMSIG_MOB_ALWAYS_APPLY_DAMAGE "mob_always_apply_damage"
|
#define COMSIG_MOB_ALWAYS_APPLY_DAMAGE "mob_always_apply_damage"
|
||||||
/// From modular_zubbers/code/modules/disease/disease_transmission.dm
|
/// From modular_zubbers/code/modules/disease/disease_transmission.dm
|
||||||
#define COMSIG_DISEASE_COUNT_UPDATE "disease_count_update"
|
#define COMSIG_DISEASE_COUNT_UPDATE "disease_count_update"
|
||||||
|
/// From [/mob/living/carbon/human/verb/safeword]: (mob/living/carbon)
|
||||||
|
#define COMSIG_OOC_ESCAPE "ooc_escape"
|
||||||
|
/// From [/datum/outfit]: (datum/outfit)
|
||||||
|
#define COMSIG_OUTFIT_EQUIP "outfit_equip"
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
#define issnail(A) (is_species(A, /datum/species/snail))
|
#define issnail(A) (is_species(A, /datum/species/snail))
|
||||||
#define isluminescent(A) (is_species(A, /datum/species/jelly/luminescent))
|
#define isluminescent(A) (is_species(A, /datum/species/jelly/luminescent))
|
||||||
#define isprimitivedemihuman(A) (is_species(A, /datum/species/human/felinid/primitive))
|
#define isprimitivedemihuman(A) (is_species(A, /datum/species/human/felinid/primitive))
|
||||||
#define isshadekin(A) (is_species(A, /datum/species/shadekin)) //BUBBERSTATION EDIT
|
#define isshadekin(A) (is_species(A, /datum/species/shadekin))
|
||||||
|
#define isprotean(A) (is_species(A, /datum/species/protean))
|
||||||
//Antags
|
//Antags
|
||||||
#define ishorrorling(A) (istype(A, /mob/living/simple_animal/hostile/true_changeling))
|
#define ishorrorling(A) (istype(A, /mob/living/simple_animal/hostile/true_changeling))
|
||||||
#define iscorticalborer(A) (istype(A, /mob/living/basic/cortical_borer))
|
#define iscorticalborer(A) (istype(A, /mob/living/basic/cortical_borer))
|
||||||
|
|||||||
@@ -303,7 +303,7 @@
|
|||||||
if(activate_msg)
|
if(activate_msg)
|
||||||
CRASH("Failed to activate [user]'s [skillchip_instance], on job [src]. Failure message: [activate_msg]")
|
CRASH("Failed to activate [user]'s [skillchip_instance], on job [src]. Failure message: [activate_msg]")
|
||||||
|
|
||||||
|
SEND_SIGNAL(user.dna.species, COMSIG_OUTFIT_EQUIP, src, visuals_only) // BUBBER EDIT: Proteans. See /datum/species/protean/proc/outfit_handling
|
||||||
user.update_body()
|
user.update_body()
|
||||||
return TRUE
|
return TRUE
|
||||||
|
|
||||||
|
|||||||
@@ -23,29 +23,30 @@
|
|||||||
if(isnull(chosen_one))
|
if(isnull(chosen_one))
|
||||||
return NOT_ENOUGH_PLAYERS
|
return NOT_ENOUGH_PLAYERS
|
||||||
//spawn the ninja and assign the candidate
|
//spawn the ninja and assign the candidate
|
||||||
|
/// BUBBER EDIT START
|
||||||
var/mob/living/carbon/human/ninja = create_space_ninja(spawn_location)
|
var/mob/living/carbon/human/ninja = create_space_ninja(spawn_location)
|
||||||
ninja.PossessByPlayer(chosen_one.key)
|
ninja.PossessByPlayer(chosen_one.key)
|
||||||
ninja.mind.add_antag_datum(/datum/antagonist/ninja)
|
ninja.mind.add_antag_datum(/datum/antagonist/ninja)
|
||||||
spawned_mobs += ninja
|
spawned_mobs += ninja
|
||||||
// SKYRAT EDIT ADDITION BEGIN: Preference Ninjas
|
if(!isprotean(ninja))
|
||||||
var/loadme = tgui_input_list(ninja, "Do you wish to load your character slot?", "Load Character?", list("Yes!", "No, I want to be random!"), default = "No, I want to be random!", timeout = 60 SECONDS)
|
var/loadme = tgui_input_list(ninja, "Do you wish to load your character slot?", "Load Character?", list("Yes!", "No, I want to be random!"), default = "No, I want to be random!", timeout = 60 SECONDS)
|
||||||
var/codename
|
var/codename
|
||||||
if(loadme == "Yes!")
|
if(loadme == "Yes!")
|
||||||
ninja.client?.prefs?.safe_transfer_prefs_to(ninja)
|
ninja.client?.prefs?.safe_transfer_prefs_to(ninja)
|
||||||
codename = tgui_input_text(ninja.client, "What should your codename be?", "Agent Name", "[pick("Master", "Legendary", "Agent", "Shinobi", "Ninja")] [ninja.dna.species.name]", 42, FALSE, TRUE, 300 SECONDS)
|
codename = tgui_input_text(ninja.client, "What should your codename be?", "Agent Name", "[pick("Master", "Legendary", "Agent", "Shinobi", "Ninja")] [ninja.dna.species.name]", 42, FALSE, TRUE, 300 SECONDS)
|
||||||
codename ? codename : (codename = "[pick("Master", "Legendary", "Agent", "Shinobi", "Ninja")] [ninja.dna.species.name]")
|
codename ? codename : (codename = "[pick("Master", "Legendary", "Agent", "Shinobi", "Ninja")] [ninja.dna.species.name]")
|
||||||
ninja.name = codename
|
ninja.name = codename
|
||||||
ninja.real_name = codename
|
ninja.real_name = codename
|
||||||
ninja.dna.update_dna_identity()
|
ninja.dna.update_dna_identity()
|
||||||
else
|
else
|
||||||
ninja.randomize_human_appearance(~(RANDOMIZE_NAME|RANDOMIZE_SPECIES))
|
ninja.randomize_human_appearance(~(RANDOMIZE_NAME|RANDOMIZE_SPECIES))
|
||||||
ninja.dna.update_dna_identity()
|
ninja.dna.update_dna_identity()
|
||||||
//BUBBER EDIT BEGIN
|
|
||||||
var/obj/item/mod/control/ninjamod = locate(/obj/item/mod/control/pre_equipped/ninja) in ninja.contents
|
var/obj/item/mod/control/ninjamod = locate(isprotean(ninja) ? /obj/item/mod/control/pre_equipped/protean : /obj/item/mod/control/pre_equipped/ninja) in ninja.contents
|
||||||
var/obj/item/mod/module/dna_lock/reinforced/ninja_dna_lock = locate(/obj/item/mod/module/dna_lock/reinforced) in ninjamod.contents
|
var/obj/item/mod/module/dna_lock/reinforced/ninja_dna_lock = locate(/obj/item/mod/module/dna_lock/reinforced) in ninjamod.contents
|
||||||
ninja_dna_lock.on_use()// BUBBER EDIT END
|
ninja_dna_lock.on_use()
|
||||||
|
/// BUBBER EDIT END
|
||||||
|
|
||||||
// SKYRAT EDIT ADDITION END: Preference Ninjas
|
|
||||||
message_admins("[ADMIN_LOOKUPFLW(ninja)] has been made into a space ninja by an event.")
|
message_admins("[ADMIN_LOOKUPFLW(ninja)] has been made into a space ninja by an event.")
|
||||||
ninja.log_message("was spawned as a ninja by an event.", LOG_GAME)
|
ninja.log_message("was spawned as a ninja by an event.", LOG_GAME)
|
||||||
|
|
||||||
|
|||||||
@@ -321,6 +321,11 @@
|
|||||||
// Makes use of tool act to prevent shoving stuff into our internal storage
|
// Makes use of tool act to prevent shoving stuff into our internal storage
|
||||||
/obj/item/mod/control/tool_act(mob/living/user, obj/item/tool, list/modifiers)
|
/obj/item/mod/control/tool_act(mob/living/user, obj/item/tool, list/modifiers)
|
||||||
if(istype(tool, /obj/item/pai_card))
|
if(istype(tool, /obj/item/pai_card))
|
||||||
|
// Bubber Edit Start - Proteans can't interface with AIs
|
||||||
|
if(istype(src, /obj/item/mod/control/pre_equipped/protean))
|
||||||
|
balloon_alert(user, "unable to interface")
|
||||||
|
return NONE
|
||||||
|
// Bubber Edit End
|
||||||
if(!open)
|
if(!open)
|
||||||
balloon_alert(user, "cover closed!")
|
balloon_alert(user, "cover closed!")
|
||||||
return NONE // shoves the card in the storage anyways
|
return NONE // shoves the card in the storage anyways
|
||||||
@@ -570,30 +575,30 @@
|
|||||||
var/check_range = TRUE
|
var/check_range = TRUE
|
||||||
return electrocute_mob(user, get_charge_source(), src, 0.7, check_range)
|
return electrocute_mob(user, get_charge_source(), src, 0.7, check_range)
|
||||||
|
|
||||||
/obj/item/mod/control/proc/install(obj/item/mod/module/new_module, mob/user)
|
/obj/item/mod/control/proc/install(obj/item/mod/module/new_module, mob/user, silent = FALSE) // Bubber Edit: Silent = FALSE
|
||||||
for(var/obj/item/mod/module/old_module as anything in modules)
|
for(var/obj/item/mod/module/old_module as anything in modules)
|
||||||
if(is_type_in_list(new_module, old_module.incompatible_modules) || is_type_in_list(old_module, new_module.incompatible_modules))
|
if(is_type_in_list(new_module, old_module.incompatible_modules) || is_type_in_list(old_module, new_module.incompatible_modules))
|
||||||
if(user)
|
if(user && !silent) // Bubber Edit: Silent arg
|
||||||
balloon_alert(user, "incompatible with [old_module]!")
|
balloon_alert(user, "incompatible with [old_module]!")
|
||||||
playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
|
playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
|
||||||
return
|
return //Bubber Edit: Return False
|
||||||
var/complexity_with_module = complexity
|
var/complexity_with_module = complexity
|
||||||
complexity_with_module += new_module.complexity
|
complexity_with_module += new_module.complexity
|
||||||
if(complexity_with_module > complexity_max)
|
if(complexity_with_module > complexity_max)
|
||||||
if(user)
|
if(user && !silent) // Bubber Edit: Silent arg
|
||||||
balloon_alert(user, "above complexity max!")
|
balloon_alert(user, "above complexity max!")
|
||||||
playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
|
playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
|
||||||
return
|
return FALSE //Bubber Edit: Return False
|
||||||
if(!new_module.has_required_parts(mod_parts))
|
if(!new_module.has_required_parts(mod_parts))
|
||||||
if(user)
|
if(user && !silent) // Bubber Edit: Silent arg
|
||||||
balloon_alert(user, "lacking required parts!")
|
balloon_alert(user, "lacking required parts!")
|
||||||
playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
|
playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
|
||||||
return
|
return FALSE //Bubber Edit:
|
||||||
if(!new_module.can_install(src))
|
if(!new_module.can_install(src))
|
||||||
if(user)
|
if(user && !silent) // Bubber Edit: Silent arg
|
||||||
balloon_alert(user, "can't install!")
|
balloon_alert(user, "can't install!")
|
||||||
playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
|
playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE)
|
||||||
return
|
return FALSE //Bubber Edit: Return False
|
||||||
new_module.forceMove(src)
|
new_module.forceMove(src)
|
||||||
modules += new_module
|
modules += new_module
|
||||||
complexity += new_module.complexity
|
complexity += new_module.complexity
|
||||||
@@ -604,10 +609,10 @@
|
|||||||
if(active && new_module.has_required_parts(mod_parts, need_active = TRUE))
|
if(active && new_module.has_required_parts(mod_parts, need_active = TRUE))
|
||||||
new_module.on_part_activation()
|
new_module.on_part_activation()
|
||||||
new_module.part_activated = TRUE
|
new_module.part_activated = TRUE
|
||||||
if(user)
|
if(user && !silent) // Bubber Edit: Silent Arg
|
||||||
balloon_alert(user, "[new_module] added")
|
balloon_alert(user, "[new_module] added")
|
||||||
playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
|
playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
|
||||||
|
return TRUE // Bubber Edit: Return True
|
||||||
/obj/item/mod/control/proc/uninstall(obj/item/mod/module/old_module, deleting = FALSE)
|
/obj/item/mod/control/proc/uninstall(obj/item/mod/module/old_module, deleting = FALSE)
|
||||||
modules -= old_module
|
modules -= old_module
|
||||||
complexity -= old_module.complexity
|
complexity -= old_module.complexity
|
||||||
|
|||||||
@@ -614,6 +614,14 @@
|
|||||||
UnregisterSignal(mod, COMSIG_ATOM_EMAG_ACT)
|
UnregisterSignal(mod, COMSIG_ATOM_EMAG_ACT)
|
||||||
|
|
||||||
/obj/item/mod/module/dna_lock/on_use()
|
/obj/item/mod/module/dna_lock/on_use()
|
||||||
|
/// Bubber Edit Start - Proteans
|
||||||
|
if(istype(mod, /obj/item/mod/control/pre_equipped/protean))
|
||||||
|
var/obj/item/mod/control/pre_equipped/protean/p_suit = mod
|
||||||
|
var/obj/item/mod/core/protean/p_core = mod.core
|
||||||
|
var/datum/species/protean/species = p_core.linked_species
|
||||||
|
if(species.owner.loc == p_suit.loc)
|
||||||
|
return balloon_alert(p_suit.wearer, "button unresponsive")
|
||||||
|
/// Bubber Edit End
|
||||||
dna = mod.wearer.dna.unique_enzymes
|
dna = mod.wearer.dna.unique_enzymes
|
||||||
balloon_alert(mod.wearer, "dna updated")
|
balloon_alert(mod.wearer, "dna updated")
|
||||||
drain_power(use_energy_cost)
|
drain_power(use_energy_cost)
|
||||||
@@ -629,6 +637,11 @@
|
|||||||
/obj/item/mod/module/dna_lock/proc/dna_check(mob/user)
|
/obj/item/mod/module/dna_lock/proc/dna_check(mob/user)
|
||||||
if(!iscarbon(user))
|
if(!iscarbon(user))
|
||||||
return FALSE
|
return FALSE
|
||||||
|
/// Bubber Edit Start
|
||||||
|
var/obj/item/mod/control/pre_equipped/protean/suit = mod
|
||||||
|
if(istype(suit))
|
||||||
|
return TRUE
|
||||||
|
/// Bubber Edit End
|
||||||
var/mob/living/carbon/carbon_user = user
|
var/mob/living/carbon/carbon_user = user
|
||||||
if(!dna || (carbon_user.has_dna() && carbon_user.dna.unique_enzymes == dna))
|
if(!dna || (carbon_user.has_dna() && carbon_user.dna.unique_enzymes == dna))
|
||||||
return TRUE
|
return TRUE
|
||||||
|
|||||||
@@ -254,6 +254,8 @@
|
|||||||
. = ..()
|
. = ..()
|
||||||
if(. != MOD_CANCEL_ACTIVATE || !isliving(user))
|
if(. != MOD_CANCEL_ACTIVATE || !isliving(user))
|
||||||
return
|
return
|
||||||
|
if(istype(mod, /obj/item/mod/control/pre_equipped/protean)) // BUBBER EDIT START
|
||||||
|
return // BUBBER EDIT END
|
||||||
if(mod.ai_assistant == user)
|
if(mod.ai_assistant == user)
|
||||||
to_chat(mod.ai_assistant, span_danger("<B>fATaL EERRoR</B>: 381200-*#00CODE <B>BLUE</B>\nAI INTErFERenCE DEtECted\nACTi0N DISrEGArdED"))
|
to_chat(mod.ai_assistant, span_danger("<B>fATaL EERRoR</B>: 381200-*#00CODE <B>BLUE</B>\nAI INTErFERenCE DEtECted\nACTi0N DISrEGArdED"))
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
/datum/surgery/blood_filter/mechanic
|
/datum/surgery/blood_filter/mechanic
|
||||||
name = "Hydraulics Purge"
|
name = "Hydraulics Purge"
|
||||||
requires_bodypart_type = BODYTYPE_ROBOTIC
|
requires_bodypart_type = BODYTYPE_ROBOTIC | BODYTYPE_NANO // BUBBER EDIT
|
||||||
steps = list(
|
steps = list(
|
||||||
/datum/surgery_step/mechanic_open,
|
/datum/surgery_step/mechanic_open,
|
||||||
/datum/surgery_step/open_hatch,
|
/datum/surgery_step/open_hatch,
|
||||||
|
|||||||
@@ -1530,7 +1530,7 @@
|
|||||||
|
|
||||||
/// The actual effect of EMPs on the limb. Allows children to override it however they want
|
/// The actual effect of EMPs on the limb. Allows children to override it however they want
|
||||||
/obj/item/bodypart/proc/emp_effect(severity, protection)
|
/obj/item/bodypart/proc/emp_effect(severity, protection)
|
||||||
if(!IS_ROBOTIC_LIMB(src))
|
if(!(IS_ROBOTIC_LIMB(src) || IS_NANO_LIMB(src))) // BUBBER EDIT - Proteans
|
||||||
return FALSE
|
return FALSE
|
||||||
// with defines at the time of writing, this is 2 brute and 1.5 burn
|
// with defines at the time of writing, this is 2 brute and 1.5 burn
|
||||||
// 2 + 1.5 = 3,5, with 6 limbs thats 21, on a heavy 42
|
// 2 + 1.5 = 3,5, with 6 limbs thats 21, on a heavy 42
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
/datum/surgery/brain_surgery/mechanic
|
/datum/surgery/brain_surgery/mechanic
|
||||||
name = "Wetware OS Diagnostics"
|
name = "Wetware OS Diagnostics"
|
||||||
requires_bodypart_type = BODYTYPE_ROBOTIC
|
requires_bodypart_type = BODYTYPE_ROBOTIC | BODYTYPE_NANO
|
||||||
possible_locs = list(BODY_ZONE_HEAD)
|
possible_locs = list(BODY_ZONE_HEAD)
|
||||||
steps = list(
|
steps = list(
|
||||||
/datum/surgery_step/mechanic_open,
|
/datum/surgery_step/mechanic_open,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
/datum/surgery/gastrectomy/mechanic
|
/datum/surgery/gastrectomy/mechanic
|
||||||
name = "Nutrient Processing System Diagnostic"
|
name = "Nutrient Processing System Diagnostic"
|
||||||
requires_bodypart_type = BODYTYPE_ROBOTIC
|
requires_bodypart_type = BODYTYPE_ROBOTIC | BODYTYPE_NANO
|
||||||
steps = list(
|
steps = list(
|
||||||
/datum/surgery_step/mechanic_open,
|
/datum/surgery_step/mechanic_open,
|
||||||
/datum/surgery_step/open_hatch,
|
/datum/surgery_step/open_hatch,
|
||||||
|
|||||||
@@ -97,7 +97,7 @@
|
|||||||
|
|
||||||
/datum/surgery/organ_manipulation/mechanic
|
/datum/surgery/organ_manipulation/mechanic
|
||||||
name = "Hardware Manipulation"
|
name = "Hardware Manipulation"
|
||||||
requires_bodypart_type = BODYTYPE_ROBOTIC
|
requires_bodypart_type = BODYTYPE_ROBOTIC | BODYTYPE_NANO
|
||||||
surgery_flags = SURGERY_SELF_OPERABLE | SURGERY_REQUIRE_LIMB | SURGERY_CHECK_TOOL_BEHAVIOUR
|
surgery_flags = SURGERY_SELF_OPERABLE | SURGERY_REQUIRE_LIMB | SURGERY_CHECK_TOOL_BEHAVIOUR
|
||||||
possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD)
|
possible_locs = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD)
|
||||||
steps = list(
|
steps = list(
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
/datum/surgery/advanced/brainwashing_sleeper/mechanic
|
/datum/surgery/advanced/brainwashing_sleeper/mechanic
|
||||||
name = "Sleeper Agent Reprogramming"
|
name = "Sleeper Agent Reprogramming"
|
||||||
desc = "Malware which directly implants the sleeper protocol directive into the robotic patient's operating system, making it their absolute priority. It can be cleared using a mindshield implant."
|
desc = "Malware which directly implants the sleeper protocol directive into the robotic patient's operating system, making it their absolute priority. It can be cleared using a mindshield implant."
|
||||||
requires_bodypart_type = BODYTYPE_ROBOTIC
|
requires_bodypart_type = BODYTYPE_ROBOTIC | BODYTYPE_NANO
|
||||||
steps = list(
|
steps = list(
|
||||||
/datum/surgery_step/mechanic_open,
|
/datum/surgery_step/mechanic_open,
|
||||||
/datum/surgery_step/open_hatch,
|
/datum/surgery_step/open_hatch,
|
||||||
|
|||||||
@@ -15,10 +15,10 @@
|
|||||||
/// far away users will be able to see, and anyone farther won't see anything.
|
/// far away users will be able to see, and anyone farther won't see anything.
|
||||||
/// Dead users will receive updates no matter what, though you likely want to add
|
/// Dead users will receive updates no matter what, though you likely want to add
|
||||||
/// a [`ui_status_only_living`] check for finer observer interactions.
|
/// a [`ui_status_only_living`] check for finer observer interactions.
|
||||||
/proc/ui_status_user_is_adjacent(mob/user, atom/source, allow_tk = TRUE)
|
/proc/ui_status_user_is_adjacent(mob/user, atom/source, allow_tk = TRUE, viewcheck = TRUE) // Bubber edit: Adds viewcheck
|
||||||
if (isliving(user))
|
if (isliving(user))
|
||||||
var/mob/living/living_user = user
|
var/mob/living/living_user = user
|
||||||
return living_user.shared_living_ui_distance(source, allow_tk = allow_tk)
|
return living_user.shared_living_ui_distance(source, viewcheck, allow_tk = allow_tk) // Bubber edit: Adds viewcheck
|
||||||
else
|
else
|
||||||
return UI_UPDATE
|
return UI_UPDATE
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 977 B |
@@ -413,6 +413,7 @@ ROUNDSTART_RACES shadekin
|
|||||||
## BUBBER RACES DOWN HERE
|
## BUBBER RACES DOWN HERE
|
||||||
ROUNDSTART_RACES lesser_abductor
|
ROUNDSTART_RACES lesser_abductor
|
||||||
ROUNDSTART_RACES zombie
|
ROUNDSTART_RACES zombie
|
||||||
|
ROUNDSTART_RACES protean
|
||||||
|
|
||||||
##-------------------------------------------------------------------------------------------
|
##-------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
mutantpart_info = list(MUTANT_INDEX_NAME = "Round", MUTANT_INDEX_COLOR_LIST = list("#FF4B19"))
|
mutantpart_info = list(MUTANT_INDEX_NAME = "Round", MUTANT_INDEX_COLOR_LIST = list("#FF4B19"))
|
||||||
slot = ORGAN_SLOT_EXTERNAL_CAP
|
slot = ORGAN_SLOT_EXTERNAL_CAP
|
||||||
preference = "feature_caps"
|
preference = "feature_caps"
|
||||||
|
organ_flags = ORGAN_EXTERNAL
|
||||||
|
|
||||||
/obj/item/organ/mushroom_cap/Initialize(mapload)
|
/obj/item/organ/mushroom_cap/Initialize(mapload)
|
||||||
if(!ispath(bodypart_overlay))
|
if(!ispath(bodypart_overlay))
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
icon = 'icons/obj/clothing/head/costume.dmi'
|
icon = 'icons/obj/clothing/head/costume.dmi'
|
||||||
icon_state = "kitty"
|
icon_state = "kitty"
|
||||||
bodypart_overlay = /datum/bodypart_overlay/mutant/ears
|
bodypart_overlay = /datum/bodypart_overlay/mutant/ears
|
||||||
|
organ_flags = ORGAN_ORGANIC | ORGAN_EXTERNAL | ORGAN_VIRGIN
|
||||||
|
|
||||||
/obj/item/organ/ears/cat
|
/obj/item/organ/ears/cat
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
preference = "feature_frills"
|
preference = "feature_frills"
|
||||||
mutantpart_key = "frills"
|
mutantpart_key = "frills"
|
||||||
mutantpart_info = list(MUTANT_INDEX_NAME = "Divinity", MUTANT_INDEX_COLOR_LIST = list("#FFFFFF"))
|
mutantpart_info = list(MUTANT_INDEX_NAME = "Divinity", MUTANT_INDEX_COLOR_LIST = list("#FFFFFF"))
|
||||||
|
organ_flags = ORGAN_EXTERNAL
|
||||||
|
|
||||||
/datum/bodypart_overlay/mutant/frills
|
/datum/bodypart_overlay/mutant/frills
|
||||||
color_source = ORGAN_COLOR_OVERRIDE
|
color_source = ORGAN_COLOR_OVERRIDE
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
preference = "feature_horns"
|
preference = "feature_horns"
|
||||||
mutantpart_key = "horns"
|
mutantpart_key = "horns"
|
||||||
mutantpart_info = list(MUTANT_INDEX_NAME = "Simple", MUTANT_INDEX_COLOR_LIST = list("#FFFFFF"))
|
mutantpart_info = list(MUTANT_INDEX_NAME = "Simple", MUTANT_INDEX_COLOR_LIST = list("#FFFFFF"))
|
||||||
|
organ_flags = ORGAN_EXTERNAL
|
||||||
|
|
||||||
/datum/bodypart_overlay/mutant/horns
|
/datum/bodypart_overlay/mutant/horns
|
||||||
layers = EXTERNAL_FRONT | EXTERNAL_ADJACENT | EXTERNAL_BEHIND
|
layers = EXTERNAL_FRONT | EXTERNAL_ADJACENT | EXTERNAL_BEHIND
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
/obj/item/organ/antennae
|
/obj/item/organ/antennae
|
||||||
mutantpart_key = "moth_antennae"
|
mutantpart_key = "moth_antennae"
|
||||||
mutantpart_info = list(MUTANT_INDEX_NAME = "Plain", MUTANT_INDEX_COLOR_LIST = list("#FFFFFF"))
|
mutantpart_info = list(MUTANT_INDEX_NAME = "Plain", MUTANT_INDEX_COLOR_LIST = list("#FFFFFF"))
|
||||||
|
organ_flags = ORGAN_UNREMOVABLE
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
zone = BODY_ZONE_HEAD
|
zone = BODY_ZONE_HEAD
|
||||||
slot = ORGAN_SLOT_EXTERNAL_POD_HAIR
|
slot = ORGAN_SLOT_EXTERNAL_POD_HAIR
|
||||||
|
organ_flags = ORGAN_EXTERNAL
|
||||||
bodypart_overlay = /datum/bodypart_overlay/mutant/pod_hair
|
bodypart_overlay = /datum/bodypart_overlay/mutant/pod_hair
|
||||||
|
|
||||||
/datum/bodypart_overlay/mutant/pod_hair
|
/datum/bodypart_overlay/mutant/pod_hair
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
zone = BODY_ZONE_HEAD
|
zone = BODY_ZONE_HEAD
|
||||||
slot = ORGAN_SLOT_EXTERNAL_SKRELL_HAIR
|
slot = ORGAN_SLOT_EXTERNAL_SKRELL_HAIR
|
||||||
|
organ_flags = ORGAN_EXTERNAL
|
||||||
preference = "feature_skrell_hair"
|
preference = "feature_skrell_hair"
|
||||||
|
|
||||||
bodypart_overlay = /datum/bodypart_overlay/mutant/skrell_hair
|
bodypart_overlay = /datum/bodypart_overlay/mutant/skrell_hair
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
zone = BODY_ZONE_HEAD
|
zone = BODY_ZONE_HEAD
|
||||||
slot = ORGAN_SLOT_EXTERNAL_SYNTH_ANTENNA
|
slot = ORGAN_SLOT_EXTERNAL_SYNTH_ANTENNA
|
||||||
|
organ_flags = ORGAN_UNREMOVABLE
|
||||||
preference = "feature_ipc_antenna"
|
preference = "feature_ipc_antenna"
|
||||||
|
|
||||||
bodypart_overlay = /datum/bodypart_overlay/mutant/synth_antenna
|
bodypart_overlay = /datum/bodypart_overlay/mutant/synth_antenna
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
zone = BODY_ZONE_HEAD
|
zone = BODY_ZONE_HEAD
|
||||||
slot = ORGAN_SLOT_EXTERNAL_SYNTH_SCREEN
|
slot = ORGAN_SLOT_EXTERNAL_SYNTH_SCREEN
|
||||||
|
organ_flags = ORGAN_EXTERNAL
|
||||||
|
|
||||||
preference = "feature_ipc_screen"
|
preference = "feature_ipc_screen"
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
mutantpart_info = list(MUTANT_INDEX_NAME = "Smooth", MUTANT_INDEX_COLOR_LIST = list("#FFFFFF"))
|
mutantpart_info = list(MUTANT_INDEX_NAME = "Smooth", MUTANT_INDEX_COLOR_LIST = list("#FFFFFF"))
|
||||||
var/can_wag = TRUE
|
var/can_wag = TRUE
|
||||||
var/wagging = FALSE
|
var/wagging = FALSE
|
||||||
|
organ_flags = ORGAN_EXTERNAL
|
||||||
|
|
||||||
/datum/bodypart_overlay/mutant/tail
|
/datum/bodypart_overlay/mutant/tail
|
||||||
color_source = ORGAN_COLOR_OVERRIDE
|
color_source = ORGAN_COLOR_OVERRIDE
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
slot = ORGAN_SLOT_WINGS
|
slot = ORGAN_SLOT_WINGS
|
||||||
mutantpart_key = "wings"
|
mutantpart_key = "wings"
|
||||||
mutantpart_info = list(MUTANT_INDEX_NAME = "Bat", MUTANT_INDEX_COLOR_LIST = list("#335533"))
|
mutantpart_info = list(MUTANT_INDEX_NAME = "Bat", MUTANT_INDEX_COLOR_LIST = list("#335533"))
|
||||||
|
organ_flags = ORGAN_EXTERNAL
|
||||||
///Whether the wings should grant flight on insertion.
|
///Whether the wings should grant flight on insertion.
|
||||||
var/unconditional_flight
|
var/unconditional_flight
|
||||||
///What species get flights thanks to those wings. Important for moth wings
|
///What species get flights thanks to those wings. Important for moth wings
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
zone = BODY_ZONE_HEAD
|
zone = BODY_ZONE_HEAD
|
||||||
slot = ORGAN_SLOT_EXTERNAL_XENODORSAL
|
slot = ORGAN_SLOT_EXTERNAL_XENODORSAL
|
||||||
|
organ_flags = ORGAN_EXTERNAL
|
||||||
|
|
||||||
preference = "feature_xenodorsal"
|
preference = "feature_xenodorsal"
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
return TRUE
|
return TRUE
|
||||||
|
|
||||||
|
|
||||||
/mob/living/verb/shift_layer_down()
|
/mob/living/verb/shift_layer_down()
|
||||||
set name = "Shift Layer Downwards"
|
set name = "Shift Layer Downwards"
|
||||||
set category = "IC"
|
set category = "IC"
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
set name = "OOC Safe Word"
|
set name = "OOC Safe Word"
|
||||||
set category = "OOC"
|
set category = "OOC"
|
||||||
set desc = "Removes any and all lewd items from you."
|
set desc = "Removes any and all lewd items from you."
|
||||||
|
SEND_SIGNAL(src, COMSIG_OOC_ESCAPE)
|
||||||
log_message("[key_name(src)] used the OOC Safe Word verb.", LOG_ATTACK)
|
log_message("[key_name(src)] used the OOC Safe Word verb.", LOG_ATTACK)
|
||||||
for(var/obj/item/equipped_item in get_equipped_items())
|
for(var/obj/item/equipped_item in get_equipped_items())
|
||||||
if(!(equipped_item.type in GLOB.pref_checked_clothes))
|
if(!(equipped_item.type in GLOB.pref_checked_clothes))
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
target_mobtypes = list(/mob/living/carbon/human)
|
target_mobtypes = list(/mob/living/carbon/human)
|
||||||
possible_locs = list(BODY_ZONE_CHEST) // The brains are in the chest
|
possible_locs = list(BODY_ZONE_CHEST) // The brains are in the chest
|
||||||
requires_bodypart_type = BODYTYPE_ROBOTIC
|
requires_bodypart_type = BODYTYPE_ROBOTIC | BODYTYPE_NANO
|
||||||
desc = "A surgical procedure that restores the default behavior logic and personality matrix of an IPC posibrain."
|
desc = "A surgical procedure that restores the default behavior logic and personality matrix of an IPC posibrain."
|
||||||
|
|
||||||
/datum/surgery/robot_brain_surgery/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
|
/datum/surgery/robot_brain_surgery/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/// Hydraulic Pump Surgery
|
/// Hydraulic Pump Surgery
|
||||||
/datum/surgery/hydraulic_maintenance
|
/datum/surgery/hydraulic_maintenance
|
||||||
name = "Hydraulic Pump Maintenance"
|
name = "Hydraulic Pump Maintenance"
|
||||||
requires_bodypart_type = BODYTYPE_ROBOTIC
|
requires_bodypart_type = BODYTYPE_ROBOTIC | BODYTYPE_NANO
|
||||||
surgery_flags = SURGERY_REQUIRE_RESTING | SURGERY_REQUIRE_LIMB | SURGERY_REQUIRES_REAL_LIMB
|
surgery_flags = SURGERY_REQUIRE_RESTING | SURGERY_REQUIRE_LIMB | SURGERY_REQUIRES_REAL_LIMB
|
||||||
steps = list(
|
steps = list(
|
||||||
/datum/surgery_step/mechanic_open,
|
/datum/surgery_step/mechanic_open,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
surgery_flags = SURGERY_REQUIRE_RESTING | SURGERY_REQUIRE_LIMB | SURGERY_REQUIRES_REAL_LIMB
|
surgery_flags = SURGERY_REQUIRE_RESTING | SURGERY_REQUIRE_LIMB | SURGERY_REQUIRES_REAL_LIMB
|
||||||
organ_to_manipulate = ORGAN_SLOT_STOMACH
|
organ_to_manipulate = ORGAN_SLOT_STOMACH
|
||||||
possible_locs = list(BODY_ZONE_CHEST)
|
possible_locs = list(BODY_ZONE_CHEST)
|
||||||
requires_bodypart_type = BODYTYPE_ROBOTIC
|
requires_bodypart_type = BODYTYPE_ROBOTIC | BODYTYPE_NANO
|
||||||
steps = list(
|
steps = list(
|
||||||
/datum/surgery_step/mechanic_open,
|
/datum/surgery_step/mechanic_open,
|
||||||
/datum/surgery_step/mechanic_unwrench,
|
/datum/surgery_step/mechanic_unwrench,
|
||||||
|
|||||||
@@ -51,3 +51,7 @@
|
|||||||
|
|
||||||
/datum/surgery/stomach_pump/mechanic/can_start(mob/user, mob/living/carbon/target)
|
/datum/surgery/stomach_pump/mechanic/can_start(mob/user, mob/living/carbon/target)
|
||||||
return !issynthetic(target) && ..()
|
return !issynthetic(target) && ..()
|
||||||
|
|
||||||
|
/// Proteans can not heal via these surgeries.
|
||||||
|
/datum/surgery/healing/can_start(mob/user, mob/living/patient)
|
||||||
|
return !isprotean(patient) && ..()
|
||||||
|
|||||||
13
modular_zubbers/code/datums/elements/nanite_organ.dm
Normal file
13
modular_zubbers/code/datums/elements/nanite_organ.dm
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Element added to some Protean organs.
|
||||||
|
|
||||||
|
/datum/element/nanite_organ
|
||||||
|
element_flags = ELEMENT_DETACH_ON_HOST_DESTROY
|
||||||
|
|
||||||
|
/datum/element/nanite_organ/Attach(obj/item/organ/target)
|
||||||
|
. = ..()
|
||||||
|
if(!istype(target) || (target.organ_flags & ORGAN_EXTERNAL))
|
||||||
|
return ELEMENT_INCOMPATIBLE
|
||||||
|
|
||||||
|
if(!istype(target, /obj/item/organ/eyes))
|
||||||
|
target.add_atom_colour(COLOR_GRAY, FIXED_COLOUR_PRIORITY) // Does a tree scream in the woods? More importantly, can you see the color of an organ that deletes itself?
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
## Title: Proteans!
|
||||||
|
|
||||||
|
This folder primarily holds all the species logic for your favorite Nanomachine friend.
|
||||||
|
|
||||||
|
## TGUI Files:
|
||||||
|
|
||||||
|
## Core Proc Edits:
|
||||||
|
- /obj/item/mod/control/tool_act()
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
- Waterpig / @Majkl-j
|
||||||
|
- The Sharkenning / @StrangeWeirdKitten
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
/**THIS FILE MAY LOOK SCARY FUTURE CONTRIBUTOR. BUT DO NOT FRET.
|
||||||
|
* These are Macros. Basically we have multiple body parts with identical logic. Each Macro will be explained bellow.
|
||||||
|
* Instead of a 500 line file with identical code, element, or compoment: a macro is used as a short cut to condense the logic.
|
||||||
|
*/
|
||||||
|
/// Brute damage messages
|
||||||
|
#define LIGHT_NANO_BRUTE "scratched"
|
||||||
|
#define MEDIUM_NANO_BRUTE "festering"
|
||||||
|
#define HEAVY_NANO_BRUTE "falling apart"
|
||||||
|
|
||||||
|
/// Burn damage messages
|
||||||
|
#define LIGHT_NANO_BURN "scorched"
|
||||||
|
#define MEDIUM_NANO_BURN "melted"
|
||||||
|
#define HEAVY_NANO_BURN "boiling"
|
||||||
|
|
||||||
|
#define BRUTE_EXAMINE_NANO "deformation"
|
||||||
|
#define BURN_EXAMINE_NANO "scorching"
|
||||||
|
|
||||||
|
/obj/item/bodypart
|
||||||
|
var/bodypart_species
|
||||||
|
|
||||||
|
/// Sprite file location
|
||||||
|
#define PROTEAN_ORGAN_SPRITE 'modular_zubbers/icons/mob/species/protean/protean.dmi'
|
||||||
|
|
||||||
|
#define PROTEAN_STOMACH_FULL 10
|
||||||
|
#define PROTEAN_STOMACH_FALTERING 0.5
|
||||||
|
#define PROTEAN_METABOLISM_RATE 2000
|
||||||
|
#define PROTEAN_LIMB_TIME 30 SECONDS
|
||||||
|
/**
|
||||||
|
* PROTEAN_BODYPART_DEFINE(path, health) Macro
|
||||||
|
* This one is very simple. It is used to give a Protean's limbs the proper bodytypes and names.
|
||||||
|
* This is an alternative to creating each /obj/item/bodypart/ parent for every Protean limb.
|
||||||
|
*/
|
||||||
|
#define PROTEAN_BODYPART_DEFINE(path, health) \
|
||||||
|
##path {\
|
||||||
|
max_damage = ##health; \
|
||||||
|
bodypart_species = SPECIES_PROTEAN; \
|
||||||
|
bodytype = BODYTYPE_NANO; \
|
||||||
|
dmg_overlay_type = "robotic"; \
|
||||||
|
brute_modifier = 0.8; \
|
||||||
|
burn_modifier = 1.2; \
|
||||||
|
light_brute_msg = LIGHT_NANO_BRUTE; \
|
||||||
|
medium_brute_msg = MEDIUM_NANO_BRUTE; \
|
||||||
|
heavy_brute_msg = HEAVY_NANO_BRUTE; \
|
||||||
|
light_burn_msg = LIGHT_NANO_BURN; \
|
||||||
|
medium_burn_msg = MEDIUM_NANO_BURN; \
|
||||||
|
heavy_burn_msg = HEAVY_NANO_BURN; \
|
||||||
|
damage_examines = list(BRUTE = BRUTE_EXAMINE_NANO, BURN = BURN_EXAMINE_NANO); \
|
||||||
|
var/qdel_timer; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PROTEAN_DELIMB_DEFINE(path) Macro
|
||||||
|
* Reworks the logic for delimbing. Once your limb gets mangled, it will fall off your body.
|
||||||
|
*/
|
||||||
|
#define PROTEAN_DELIMB_DEFINE(path) \
|
||||||
|
##path/try_dismember(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus) {\
|
||||||
|
if(((get_damage() + wounding_dmg) >= max_damage)) {\
|
||||||
|
dismember(); \
|
||||||
|
qdel_timer = QDEL_IN_STOPPABLE(src, PROTEAN_LIMB_TIME); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PROTEAN_LIMB_ATTACH(path) Macro
|
||||||
|
* If you reattch your limb, it will delete the qdel timer.
|
||||||
|
*/
|
||||||
|
#define PROTEAN_LIMB_ATTACH(path) \
|
||||||
|
##path/can_attach_limb(limb_owner, special) {\
|
||||||
|
. = ..(); \
|
||||||
|
if(!.) {\
|
||||||
|
return FALSE; \
|
||||||
|
} \
|
||||||
|
if(!isnull(qdel_timer)) { \
|
||||||
|
deltimer(qdel_timer); \
|
||||||
|
return TRUE; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
// Core
|
||||||
|
PROTEAN_BODYPART_DEFINE(/obj/item/bodypart/head/mutant/protean, 120)
|
||||||
|
PROTEAN_DELIMB_DEFINE(/obj/item/bodypart/head/mutant/protean)
|
||||||
|
PROTEAN_LIMB_ATTACH(/obj/item/bodypart/head/mutant/protean)
|
||||||
|
|
||||||
|
PROTEAN_BODYPART_DEFINE(/obj/item/bodypart/chest/mutant/protean, LIMB_MAX_HP_CORE)
|
||||||
|
|
||||||
|
|
||||||
|
// Limbs
|
||||||
|
PROTEAN_BODYPART_DEFINE(/obj/item/bodypart/arm/left/mutant/protean, 40)
|
||||||
|
PROTEAN_BODYPART_DEFINE(/obj/item/bodypart/arm/right/mutant/protean, 40)
|
||||||
|
|
||||||
|
/// Legs are a little more special, so they're not macro'd
|
||||||
|
/obj/item/bodypart/leg/right/mutant/protean
|
||||||
|
max_damage = 40
|
||||||
|
bodypart_species = SPECIES_PROTEAN
|
||||||
|
bodytype = BODYTYPE_NANO
|
||||||
|
dmg_overlay_type = "robotic"
|
||||||
|
brute_modifier = 0.8
|
||||||
|
burn_modifier = 1.2
|
||||||
|
light_brute_msg = LIGHT_NANO_BRUTE
|
||||||
|
medium_brute_msg = MEDIUM_NANO_BRUTE
|
||||||
|
heavy_brute_msg = HEAVY_NANO_BRUTE
|
||||||
|
light_burn_msg = LIGHT_NANO_BURN
|
||||||
|
medium_burn_msg = MEDIUM_NANO_BURN
|
||||||
|
heavy_burn_msg = HEAVY_NANO_BRUTE
|
||||||
|
damage_examines = list(BRUTE = BRUTE_EXAMINE_NANO, BURN = BURN_EXAMINE_NANO)
|
||||||
|
digitigrade_type = /obj/item/bodypart/leg/right/mutant/protean/digitigrade
|
||||||
|
var/qdel_timer
|
||||||
|
|
||||||
|
/obj/item/bodypart/leg/left/mutant/protean
|
||||||
|
max_damage = 40
|
||||||
|
bodypart_species = SPECIES_PROTEAN
|
||||||
|
bodytype = BODYTYPE_NANO
|
||||||
|
dmg_overlay_type = "robotic"
|
||||||
|
brute_modifier = 0.8
|
||||||
|
burn_modifier = 1.2
|
||||||
|
light_brute_msg = LIGHT_NANO_BRUTE
|
||||||
|
medium_brute_msg = MEDIUM_NANO_BRUTE
|
||||||
|
heavy_brute_msg = HEAVY_NANO_BRUTE
|
||||||
|
light_burn_msg = LIGHT_NANO_BURN
|
||||||
|
medium_burn_msg = MEDIUM_NANO_BURN
|
||||||
|
heavy_burn_msg = HEAVY_NANO_BRUTE
|
||||||
|
damage_examines = list(BRUTE = BRUTE_EXAMINE_NANO, BURN = BURN_EXAMINE_NANO)
|
||||||
|
digitigrade_type = /obj/item/bodypart/leg/left/mutant/protean/digitigrade
|
||||||
|
var/qdel_timer
|
||||||
|
|
||||||
|
/obj/item/bodypart/leg/right/mutant/protean/digitigrade
|
||||||
|
icon_greyscale = BODYPART_ICON_MAMMAL
|
||||||
|
limb_id = BODYPART_ID_DIGITIGRADE
|
||||||
|
bodyshape = parent_type::bodyshape | BODYSHAPE_DIGITIGRADE
|
||||||
|
base_limb_id = BODYPART_ID_DIGITIGRADE
|
||||||
|
|
||||||
|
/obj/item/bodypart/leg/right/mutant/protean/digitigrade/update_limb(dropping_limb = FALSE, is_creating = FALSE)
|
||||||
|
. = ..()
|
||||||
|
check_mutant_compatability()
|
||||||
|
|
||||||
|
/obj/item/bodypart/leg/left/mutant/protean/digitigrade
|
||||||
|
icon_greyscale = BODYPART_ICON_MAMMAL
|
||||||
|
limb_id = BODYPART_ID_DIGITIGRADE
|
||||||
|
bodyshape = parent_type::bodyshape | BODYSHAPE_DIGITIGRADE
|
||||||
|
base_limb_id = BODYPART_ID_DIGITIGRADE
|
||||||
|
|
||||||
|
/obj/item/bodypart/leg/left/mutant/protean/digitigrade/update_limb(dropping_limb = FALSE, is_creating = FALSE)
|
||||||
|
. = ..()
|
||||||
|
check_mutant_compatability()
|
||||||
|
|
||||||
|
PROTEAN_DELIMB_DEFINE(/obj/item/bodypart/arm/left/mutant/protean)
|
||||||
|
PROTEAN_DELIMB_DEFINE(/obj/item/bodypart/arm/right/mutant/protean)
|
||||||
|
PROTEAN_DELIMB_DEFINE(/obj/item/bodypart/leg/left/mutant/protean)
|
||||||
|
PROTEAN_DELIMB_DEFINE(/obj/item/bodypart/leg/right/mutant/protean)
|
||||||
|
|
||||||
|
PROTEAN_LIMB_ATTACH(/obj/item/bodypart/arm/left/mutant/protean)
|
||||||
|
PROTEAN_LIMB_ATTACH(/obj/item/bodypart/arm/right/mutant/protean)
|
||||||
|
PROTEAN_LIMB_ATTACH(/obj/item/bodypart/leg/left/mutant/protean)
|
||||||
|
PROTEAN_LIMB_ATTACH(/obj/item/bodypart/leg/right/mutant/protean)
|
||||||
|
|
||||||
|
#undef PROTEAN_BODYPART_DEFINE
|
||||||
|
#undef PROTEAN_DELIMB_DEFINE
|
||||||
|
#undef PROTEAN_LIMB_ATTACH
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
/datum/species/protean
|
||||||
|
id = SPECIES_PROTEAN
|
||||||
|
examine_limb_id = SPECIES_PROTEAN
|
||||||
|
|
||||||
|
name = "Protean"
|
||||||
|
sexes = TRUE
|
||||||
|
|
||||||
|
siemens_coeff = 1.5 // Electricty messes you up.
|
||||||
|
payday_modifier = 0.7 // 30 percent poorer
|
||||||
|
|
||||||
|
exotic_blood = /datum/reagent/medicine/nanite_slurry
|
||||||
|
digitigrade_customization = DIGITIGRADE_OPTIONAL
|
||||||
|
|
||||||
|
meat = /obj/item/stack/sheet/iron
|
||||||
|
|
||||||
|
mutant_bodyparts = list()
|
||||||
|
mutantbrain = /obj/item/organ/brain/protean
|
||||||
|
mutantheart = /obj/item/organ/heart/protean
|
||||||
|
mutantstomach = /obj/item/organ/stomach/protean
|
||||||
|
mutantlungs = null
|
||||||
|
mutantliver = null
|
||||||
|
mutantappendix = null
|
||||||
|
mutanteyes = /obj/item/organ/eyes/robotic/protean
|
||||||
|
mutantears = /obj/item/organ/ears/cybernetic/protean
|
||||||
|
mutanttongue = /obj/item/organ/tongue/cybernetic/protean
|
||||||
|
|
||||||
|
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT
|
||||||
|
|
||||||
|
bodypart_overrides = list(
|
||||||
|
BODY_ZONE_HEAD = /obj/item/bodypart/head/mutant/protean,
|
||||||
|
BODY_ZONE_CHEST = /obj/item/bodypart/chest/mutant/protean,
|
||||||
|
BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/mutant/protean,
|
||||||
|
BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/mutant/protean,
|
||||||
|
BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/mutant/protean,
|
||||||
|
BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/mutant/protean,
|
||||||
|
)
|
||||||
|
|
||||||
|
inherent_traits = list(
|
||||||
|
// Default Species
|
||||||
|
TRAIT_ADVANCEDTOOLUSER,
|
||||||
|
TRAIT_CAN_STRIP,
|
||||||
|
TRAIT_LITERATE,
|
||||||
|
TRAIT_MUTANT_COLORS,
|
||||||
|
|
||||||
|
// Needed to exist without dying and robot specific stuff.
|
||||||
|
TRAIT_NOBREATH,
|
||||||
|
TRAIT_LIVERLESS_METABOLISM,
|
||||||
|
TRAIT_ROCK_EATER,
|
||||||
|
TRAIT_STABLEHEART, // TODO: handle orchestrator code
|
||||||
|
TRAIT_NOHUNGER, // They will have metal stored in the stomach. Fuck nutrition code.
|
||||||
|
TRAIT_LIMBATTACHMENT,
|
||||||
|
|
||||||
|
// Synthetic lifeforms
|
||||||
|
TRAIT_GENELESS,
|
||||||
|
TRAIT_NO_HUSK,
|
||||||
|
TRAIT_NO_DNA_SCRAMBLE,
|
||||||
|
TRAIT_SYNTHETIC, // Not used in any code, but just in case
|
||||||
|
TRAIT_TOXIMMUNE,
|
||||||
|
TRAIT_NEVER_WOUNDED, // Does not wound.
|
||||||
|
|
||||||
|
// Extra cool stuff
|
||||||
|
TRAIT_RADIMMUNE,
|
||||||
|
TRAIT_EASYDISMEMBER,
|
||||||
|
TRAIT_RDS_SUPPRESSED,
|
||||||
|
TRAIT_MADNESS_IMMUNE,
|
||||||
|
|
||||||
|
// Seperate handling will be used. Proteans never truely "die". They get stuck in their suit.
|
||||||
|
TRAIT_NODEATH,
|
||||||
|
|
||||||
|
//TRAIT_VENTCRAWLER_NUDE, - A tease. If you want to give a species vent crawl. God help your soul. But I won't stop you from learning that hard lesson.
|
||||||
|
)
|
||||||
|
|
||||||
|
inherent_biotypes = MOB_ROBOTIC | MOB_HUMANOID
|
||||||
|
reagent_flags = null
|
||||||
|
|
||||||
|
/// Reference to the
|
||||||
|
var/obj/item/mod/control/pre_equipped/protean/species_modsuit
|
||||||
|
|
||||||
|
/// Reference to the species owner
|
||||||
|
var/mob/living/carbon/human/owner
|
||||||
|
|
||||||
|
/mob/living/carbon/human/species/protean
|
||||||
|
race = /datum/species/protean
|
||||||
|
|
||||||
|
/datum/species/protean/Destroy(force)
|
||||||
|
QDEL_NULL(species_modsuit)
|
||||||
|
owner = null
|
||||||
|
. = ..()
|
||||||
|
|
||||||
|
/datum/species/protean/on_species_gain(mob/living/carbon/human/gainer, datum/species/old_species, pref_load, regenerate_icons = TRUE)
|
||||||
|
. = ..()
|
||||||
|
owner = gainer
|
||||||
|
equip_modsuit(gainer)
|
||||||
|
RegisterSignal(src, COMSIG_OUTFIT_EQUIP, PROC_REF(outfit_handling))
|
||||||
|
var/obj/item/mod/core/protean/core = species_modsuit.core
|
||||||
|
core?.linked_species = src
|
||||||
|
var/static/protean_verbs = list(
|
||||||
|
/mob/living/carbon/proc/protean_ui,
|
||||||
|
/mob/living/carbon/proc/protean_heal,
|
||||||
|
/mob/living/carbon/proc/lock_suit,
|
||||||
|
/mob/living/carbon/proc/suit_transformation,
|
||||||
|
)
|
||||||
|
add_verb(gainer, protean_verbs)
|
||||||
|
|
||||||
|
/datum/species/protean/on_species_loss(mob/living/carbon/human/gainer, datum/species/new_species, pref_load)
|
||||||
|
. = ..()
|
||||||
|
if(species_modsuit.stored_modsuit)
|
||||||
|
species_modsuit.unassimilate_modsuit(owner, TRUE)
|
||||||
|
gainer.dropItemToGround(species_modsuit, TRUE)
|
||||||
|
if(species_modsuit)
|
||||||
|
QDEL_NULL(species_modsuit)
|
||||||
|
owner = null
|
||||||
|
|
||||||
|
/datum/species/protean/proc/equip_modsuit(mob/living/carbon/human/gainer)
|
||||||
|
species_modsuit = new()
|
||||||
|
var/obj/item/item_in_slot = gainer.get_item_by_slot(ITEM_SLOT_BACK)
|
||||||
|
if(item_in_slot)
|
||||||
|
if(HAS_TRAIT(item_in_slot, TRAIT_NODROP))
|
||||||
|
stack_trace("Protean modsuit forced dropped a TRAIT_NODROP item on species equip. Type: [item_in_slot]")
|
||||||
|
gainer.dropItemToGround(item_in_slot, force = TRUE)
|
||||||
|
return gainer.equip_to_slot_if_possible(species_modsuit, ITEM_SLOT_BACK, disable_warning = TRUE)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protean Outfit Handling and Logic ----------------------------------------
|
||||||
|
* Proteans get really fucky with outfit logic, so I've appended a COMSIG_OUTFIT_EQUIP signal at the end of /datum/outfit/proc/equip.
|
||||||
|
* Basically what this does, is once outfit code has been ran, it will go through the assigned outfit again.
|
||||||
|
* It assimilates any modsuits, gives you a storage if you're missing it, and places contents into said storage.
|
||||||
|
* Yes, this is really snowflakey but I've been bashing my head against the wall for 4 hours trying to figure this out.
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/datum/species/protean/proc/outfit_handling(datum/species/protean, datum/outfit/outfit, visuals_only) // Very snowflakey code. I'm not making outfits for every job.
|
||||||
|
SIGNAL_HANDLER
|
||||||
|
var/get_a_job = istype(outfit, /datum/outfit/job)
|
||||||
|
var/obj/item/mod/control/suit
|
||||||
|
if(ispath(outfit.back, /obj/item/mod/control))
|
||||||
|
var/control_path = outfit.back
|
||||||
|
suit = new control_path()
|
||||||
|
INVOKE_ASYNC(species_modsuit, TYPE_PROC_REF(/obj/item/mod/control/pre_equipped/protean, assimilate_modsuit), owner, suit, TRUE)
|
||||||
|
INVOKE_ASYNC(species_modsuit, TYPE_PROC_REF(/obj/item/mod/control, quick_activation))
|
||||||
|
|
||||||
|
var/obj/item/mod/module/storage/storage = locate() in species_modsuit.modules // Give a storage if we don't have one.
|
||||||
|
if(!storage)
|
||||||
|
storage = new()
|
||||||
|
species_modsuit.install(storage, owner, TRUE)
|
||||||
|
|
||||||
|
if(outfit.backpack_contents)
|
||||||
|
outfit.backpack_contents += /obj/item/stack/sheet/iron/twenty
|
||||||
|
for(var/path in outfit.backpack_contents)
|
||||||
|
if(!get_a_job)
|
||||||
|
continue
|
||||||
|
var/number = outfit.backpack_contents[path]
|
||||||
|
if(!isnum(number))//Default to 1
|
||||||
|
number = 1
|
||||||
|
for(var/i in 1 to number) // Copy and paste of EQUIP_OUTFIT_ITEM
|
||||||
|
owner.equip_to_slot_or_del(SSwardrobe.provide_type(path, owner), ITEM_SLOT_BACKPACK, TRUE, TRUE)
|
||||||
|
var/obj/item/outfit_item = owner.get_item_by_slot(ITEM_SLOT_BACKPACK)
|
||||||
|
if(outfit_item && outfit_item.type == path && !get_a_job)
|
||||||
|
outfit_item.on_outfit_equip(owner, visuals_only, ITEM_SLOT_BACKPACK)
|
||||||
|
|
||||||
|
/datum/species/protean/get_default_mutant_bodyparts()
|
||||||
|
return list(
|
||||||
|
"legs" = list("Normal Legs", FALSE)
|
||||||
|
)
|
||||||
|
|
||||||
|
/datum/species/protean/allows_food_preferences()
|
||||||
|
return FALSE
|
||||||
|
|
||||||
|
/datum/species/protean/get_species_description()
|
||||||
|
return list(
|
||||||
|
"Trillions of small machines swarm into a single crewmember. This is a Protean, a walking coherent blob of metallic mass, and a churning factory that turns materials into more of itself. \
|
||||||
|
Proteans are unkillable. Instead, they shunt themselves away into their core when catastrophic losses to their swarm occur. Their cores also mimic the functions of a modsuit and can even assimilate more functional suits to use. \
|
||||||
|
Proteans only have a few vital organs, which can only be replaced via cargo. Their refactory is a miniature factory, and without it, they will face slow, agonizing degradation. Their Orchestrator is a miniature processor required for ease of movement. \
|
||||||
|
Proteans are an extremely fragile species, weak in combat, but a powerful aid, or a puppeteer pulling the strings.")
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
/**
|
||||||
|
* HANDLES ALL OF PROTEAN EXISTENCE CODE.
|
||||||
|
* Very snowflakey species. This is the communication chain.
|
||||||
|
* Brain > Refactory > Modsuit Core > Modsuit
|
||||||
|
* Brain > Orchestrator
|
||||||
|
* Brain > Murder every other organ you try to shove in this thing.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/obj/item/organ/brain/protean
|
||||||
|
name = "protean core"
|
||||||
|
desc = "An advanced positronic brain, typically found in the core of a protean"
|
||||||
|
icon = PROTEAN_ORGAN_SPRITE
|
||||||
|
icon_state = "posi1"
|
||||||
|
zone = BODY_ZONE_CHEST
|
||||||
|
organ_flags = ORGAN_ROBOTIC | ORGAN_NANOMACHINE
|
||||||
|
organ_traits = list(TRAIT_SILICON_EMOTES_ALLOWED)
|
||||||
|
/// Whether or not the protean is stuck in their suit or not.
|
||||||
|
var/dead = FALSE
|
||||||
|
COOLDOWN_DECLARE(message_cooldown)
|
||||||
|
COOLDOWN_DECLARE(refactory_cooldown)
|
||||||
|
COOLDOWN_DECLARE(orchestrator_cooldown)
|
||||||
|
|
||||||
|
/obj/item/organ/brain/protean/on_life(seconds_per_tick, times_fired)
|
||||||
|
. = ..()
|
||||||
|
if(dead)
|
||||||
|
return
|
||||||
|
var/damage_amount = 4 // How much damage per life() tick this organ should apply
|
||||||
|
|
||||||
|
for(var/obj/item/organ/organ in owner.organs)
|
||||||
|
if(organ.organ_flags & (ORGAN_ROBOTIC | ORGAN_NANOMACHINE | ORGAN_EXTERNAL | ORGAN_UNREMOVABLE))
|
||||||
|
continue
|
||||||
|
if(istype(organ, /obj/item/organ/taur_body))
|
||||||
|
continue
|
||||||
|
organ.apply_organ_damage(damage_amount)
|
||||||
|
if(COOLDOWN_FINISHED(src, message_cooldown))
|
||||||
|
to_chat(owner, span_warning("Your mass violently rips apart [organ]!"))
|
||||||
|
COOLDOWN_START(src, message_cooldown, 30 SECONDS)
|
||||||
|
if(organ.organ_flags & ORGAN_FAILING)
|
||||||
|
to_chat(owner, span_warning("Your mass violently rejects [organ]"))
|
||||||
|
organ.mob_remove(owner, TRUE)
|
||||||
|
|
||||||
|
handle_refactory(owner.get_organ_slot(ORGAN_SLOT_STOMACH))
|
||||||
|
handle_orchestrator(owner.get_organ_slot(ORGAN_SLOT_HEART))
|
||||||
|
if(owner.stat >= HARD_CRIT && !dead)
|
||||||
|
to_chat(owner, span_red("Your fragile refactory withers away with your mass reduced to scraps. Someone will have to help you."))
|
||||||
|
dead = TRUE
|
||||||
|
owner.fully_heal(HEAL_DAMAGE)
|
||||||
|
qdel(owner.get_organ_slot(ORGAN_SLOT_STOMACH))
|
||||||
|
go_into_suit(TRUE)
|
||||||
|
|
||||||
|
/obj/item/organ/brain/protean/proc/handle_refactory(obj/item/organ) // Slowly degrade
|
||||||
|
var/datum/species/protean/species = owner?.dna.species
|
||||||
|
var/obj/item/mod/control/pre_equipped/protean/suit = species.species_modsuit
|
||||||
|
if(owner.loc == suit)
|
||||||
|
return
|
||||||
|
if(isnull(organ) || !istype(organ, /obj/item/organ/stomach/protean))
|
||||||
|
owner.adjustBruteLoss(3, forced = TRUE)
|
||||||
|
if(COOLDOWN_FINISHED(src, refactory_cooldown))
|
||||||
|
to_chat(owner, span_warning("Your mass is slowly degrading without your refactory!"))
|
||||||
|
COOLDOWN_START(src, refactory_cooldown, 30 SECONDS)
|
||||||
|
|
||||||
|
/obj/item/organ/brain/protean/proc/handle_orchestrator(obj/item/organ) // If you're missing an orchestrator, you will have trouble walking.
|
||||||
|
var/datum/species/protean/species = owner?.dna.species
|
||||||
|
var/obj/item/mod/control/pre_equipped/protean/suit = species.species_modsuit
|
||||||
|
if(owner.loc == suit)
|
||||||
|
return
|
||||||
|
if(!COOLDOWN_FINISHED(src, orchestrator_cooldown))
|
||||||
|
return
|
||||||
|
|
||||||
|
if(isnull(organ) || !istype(organ, /obj/item/organ/heart/protean))
|
||||||
|
owner.KnockToFloor(TRUE, TRUE, 1 SECONDS)
|
||||||
|
owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/protean_slowdown, multiplicative_slowdown = 2)
|
||||||
|
to_chat(owner, span_warning("You're struggling to walk without your orchestrator!"))
|
||||||
|
else
|
||||||
|
owner.remove_movespeed_modifier(/datum/movespeed_modifier/protean_slowdown)
|
||||||
|
|
||||||
|
COOLDOWN_START(src, orchestrator_cooldown, 30 SECONDS)
|
||||||
|
|
||||||
|
/datum/movespeed_modifier/protean_slowdown
|
||||||
|
variable = TRUE
|
||||||
|
|
||||||
|
/obj/item/organ/brain/protean/proc/go_into_suit(forced)
|
||||||
|
var/datum/species/protean/protean = owner.dna?.species
|
||||||
|
if(!istype(protean) || owner.loc == protean.species_modsuit)
|
||||||
|
return
|
||||||
|
if(!forced)
|
||||||
|
if(!do_after(owner, 5 SECONDS))
|
||||||
|
return
|
||||||
|
owner.extinguish_mob()
|
||||||
|
var/obj/item/mod/control/pre_equipped/protean/suit = protean.species_modsuit
|
||||||
|
owner.invisibility = 101
|
||||||
|
new /obj/effect/temp_visual/protean_to_suit(owner.loc, owner.dir)
|
||||||
|
owner.Stun(INFINITY, TRUE)
|
||||||
|
suit.drop_suit()
|
||||||
|
owner.forceMove(suit)
|
||||||
|
sleep(12) //Sleep is fine here because I'm not returning anything and if the brain gets deleted within 12 ticks of this being ran, we have some other serious issues.
|
||||||
|
owner.invisibility = initial(owner.invisibility)
|
||||||
|
|
||||||
|
/obj/item/organ/brain/protean/proc/leave_modsuit()
|
||||||
|
var/datum/species/protean/protean = owner.dna?.species
|
||||||
|
if(!istype(protean))
|
||||||
|
return
|
||||||
|
var/obj/item/mod/control/pre_equipped/protean/suit = protean.species_modsuit
|
||||||
|
if(dead)
|
||||||
|
to_chat(owner, span_warning("Your mass is destroyed. You are unable to leave."))
|
||||||
|
return
|
||||||
|
if(!do_after(owner, 5 SECONDS, suit, IGNORE_INCAPACITATED))
|
||||||
|
return
|
||||||
|
var/mob/living/carbon/mob = suit.loc
|
||||||
|
if(istype(mob))
|
||||||
|
mob.dropItemToGround(suit, TRUE)
|
||||||
|
var/datum/storage/storage = suit.loc.atom_storage
|
||||||
|
if(istype(storage))
|
||||||
|
storage.remove_single(null, suit, get_turf(suit), TRUE)
|
||||||
|
suit.invisibility = 101
|
||||||
|
new /obj/effect/temp_visual/protean_from_suit(suit.loc, owner.dir)
|
||||||
|
sleep(12) //Same as above
|
||||||
|
suit.drop_suit()
|
||||||
|
owner.forceMove(suit.loc)
|
||||||
|
if(owner.get_item_by_slot(ITEM_SLOT_BACK))
|
||||||
|
owner.dropItemToGround(owner.get_item_by_slot(ITEM_SLOT_BACK), TRUE, TRUE, TRUE)
|
||||||
|
owner.equip_to_slot_if_possible(suit, ITEM_SLOT_BACK, disable_warning = TRUE)
|
||||||
|
suit.invisibility = initial(suit.invisibility)
|
||||||
|
addtimer(CALLBACK(owner, TYPE_PROC_REF(/mob/living, SetStun), 0), 5 SECONDS)
|
||||||
|
if(!HAS_TRAIT(suit, TRAIT_NODROP))
|
||||||
|
ADD_TRAIT(suit, TRAIT_NODROP, "protean")
|
||||||
|
|
||||||
|
/obj/item/organ/brain/protean/proc/replace_limbs()
|
||||||
|
var/obj/item/organ/stomach/protean/stomach = owner.get_organ_slot(ORGAN_SLOT_STOMACH)
|
||||||
|
var/obj/item/organ/eyes/robotic/protean/eyes = owner.get_organ_slot(ORGAN_SLOT_EYES)
|
||||||
|
var/obj/item/organ/tongue/cybernetic/protean/tongue = owner.get_organ_slot(ORGAN_SLOT_TONGUE)
|
||||||
|
var/obj/item/organ/ears/cybernetic/protean/ears = owner.get_organ_slot(ORGAN_SLOT_EARS)
|
||||||
|
|
||||||
|
if(stomach.metal <= PROTEAN_STOMACH_FULL * 0.6 && istype(stomach))
|
||||||
|
to_chat(owner, span_warning("Not enough metal to heal body!"))
|
||||||
|
return
|
||||||
|
if(!istype(owner.loc, /obj/item/mod/control))
|
||||||
|
to_chat(owner, span_warning("Not in the open. You must be inside your suit!"))
|
||||||
|
return
|
||||||
|
var/datum/species/protean/species = owner.dna.species
|
||||||
|
if(!do_after(owner, 30 SECONDS, species.species_modsuit, IGNORE_INCAPACITATED))
|
||||||
|
return
|
||||||
|
|
||||||
|
stomach.metal = clamp(stomach.metal - (PROTEAN_STOMACH_FULL * 0.6), 0, 10)
|
||||||
|
owner.fully_heal(HEAL_LIMBS)
|
||||||
|
if(isnull(eyes))
|
||||||
|
eyes = new /obj/item/organ/eyes/robotic/protean
|
||||||
|
eyes.on_bodypart_insert()
|
||||||
|
eyes.Insert(owner, TRUE)
|
||||||
|
|
||||||
|
if(isnull(tongue))
|
||||||
|
tongue = new /obj/item/organ/tongue/cybernetic/protean
|
||||||
|
tongue.on_bodypart_insert()
|
||||||
|
tongue.Insert(owner, TRUE)
|
||||||
|
|
||||||
|
if(isnull(ears))
|
||||||
|
ears = new /obj/item/organ/ears/cybernetic/protean
|
||||||
|
ears.on_bodypart_insert()
|
||||||
|
ears.Insert(owner, TRUE)
|
||||||
|
|
||||||
|
/obj/item/organ/brain/protean/proc/revive()
|
||||||
|
dead = FALSE
|
||||||
|
playsound(owner, 'sound/machines/ping.ogg', 30)
|
||||||
|
to_chat(owner, span_warning("You have regained all your mass!"))
|
||||||
|
owner.fully_heal()
|
||||||
|
|
||||||
|
/obj/item/organ/brain/protean/proc/revive_timer()
|
||||||
|
balloon_alert_to_viewers("repairing")
|
||||||
|
addtimer(CALLBACK(src, PROC_REF(revive)), 5 MINUTES) // Bump to 5 minutes
|
||||||
|
|
||||||
|
/obj/effect/temp_visual/protean_to_suit
|
||||||
|
name = "to_suit"
|
||||||
|
icon = PROTEAN_ORGAN_SPRITE
|
||||||
|
icon_state = "to_puddle"
|
||||||
|
duration = 12
|
||||||
|
|
||||||
|
/obj/effect/temp_visual/protean_from_suit
|
||||||
|
name = "from_suit"
|
||||||
|
icon = PROTEAN_ORGAN_SPRITE
|
||||||
|
icon_state = "from_puddle"
|
||||||
|
duration = 12
|
||||||
@@ -0,0 +1,287 @@
|
|||||||
|
/obj/item/mod/control/pre_equipped/protean
|
||||||
|
name = "hardsuit rig"
|
||||||
|
desc = "The hardsuit rig unit of a Protean, allowing them to retract into it, or to deploy a suit that protects against various environments."
|
||||||
|
theme = /datum/mod_theme // Standard theme. TODO: Can be changed with standard mod armors
|
||||||
|
|
||||||
|
applied_core = /obj/item/mod/core/protean
|
||||||
|
applied_cell = null // Goes off stomach
|
||||||
|
resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF // funny nanite
|
||||||
|
/// Whether or not the wearer can undeploy parts.
|
||||||
|
var/modlocked = FALSE
|
||||||
|
var/obj/item/mod/control/stored_modsuit
|
||||||
|
var/datum/mod_theme/stored_theme
|
||||||
|
|
||||||
|
/datum/mod_theme/protean
|
||||||
|
name = "Protean"
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/Initialize(mapload, datum/mod_theme/new_theme, new_skin, obj/item/mod/core/new_core)
|
||||||
|
. = ..()
|
||||||
|
ADD_TRAIT(src, TRAIT_NODROP, "protean")
|
||||||
|
AddElement(/datum/element/strippable/protean, GLOB.strippable_human_items, TYPE_PROC_REF(/mob/living/carbon/human/, should_strip))
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/Destroy()
|
||||||
|
if(stored_modsuit)
|
||||||
|
drop_suit()
|
||||||
|
INVOKE_ASYNC(src, PROC_REF(unassimilate_modsuit), null, forced = TRUE)
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/wrench_act(mob/living/user, obj/item/wrench)
|
||||||
|
return FALSE // Can't remove the core.
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/emag_act(mob/user, obj/item/card/emag/emag_card)
|
||||||
|
return FALSE // Nope
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/canStrip(mob/who)
|
||||||
|
return TRUE
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/doStrip(mob/stripper, mob/owner) // Custom stripping code.
|
||||||
|
if(!isprotean(wearer)) // Strip it normally
|
||||||
|
REMOVE_TRAIT(src, TRAIT_NODROP, "protean") // Your ass is coming off.
|
||||||
|
return ..()
|
||||||
|
var/obj/item/mod/module/storage/inventory = locate() in src.modules
|
||||||
|
if(!isnull(inventory))
|
||||||
|
src.atom_storage.remove_all()
|
||||||
|
to_chat(stripper, span_notice("You empty out all the items from the Protean's internal storage module!"))
|
||||||
|
stripper.balloon_alert(stripper, "emptied storage")
|
||||||
|
return TRUE
|
||||||
|
|
||||||
|
to_chat(stripper, span_warning("This suit seems to be a part of them. You can't remove it!"))
|
||||||
|
stripper.balloon_alert(stripper, "can't strip a protean's suit!")
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/proc/drop_suit()
|
||||||
|
if(wearer)
|
||||||
|
if(HAS_TRAIT(src, TRAIT_NODROP))
|
||||||
|
REMOVE_TRAIT(src, TRAIT_NODROP, "protean")
|
||||||
|
wearer.dropItemToGround(src, TRUE, TRUE, TRUE)
|
||||||
|
|
||||||
|
/// Proteans can lock themselves on people.
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/proc/toggle_lock(forced = FALSE)
|
||||||
|
if(modlocked && !forced && !isprotean(wearer))
|
||||||
|
REMOVE_TRAIT(src, TRAIT_NODROP, "protean")
|
||||||
|
modlocked = !modlocked
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/equipped(mob/user, slot, initial)
|
||||||
|
. = ..()
|
||||||
|
|
||||||
|
if(isprotean(wearer))
|
||||||
|
return
|
||||||
|
if(slot == ITEM_SLOT_BACK && wearer)
|
||||||
|
RegisterSignal(wearer, COMSIG_OOC_ESCAPE, PROC_REF(ooc_escape))
|
||||||
|
if(modlocked)
|
||||||
|
ADD_TRAIT(src, TRAIT_NODROP, "protean")
|
||||||
|
to_chat(wearer, span_warning("The suit does not seem to be able to come off..."))
|
||||||
|
else
|
||||||
|
UnregisterSignal(wearer, COMSIG_OOC_ESCAPE)
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/choose_deploy(mob/user)
|
||||||
|
if(!isprotean(user) && modlocked && active)
|
||||||
|
balloon_alert(user, "it refuses to listen")
|
||||||
|
return FALSE
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/toggle_activate(mob/user, force_deactivate)
|
||||||
|
if(!force_deactivate && modlocked && !isprotean(user) && active)
|
||||||
|
balloon_alert(user, "it doesn't turn off")
|
||||||
|
return FALSE
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/quick_deploy(mob/user)
|
||||||
|
if(!isprotean(user) && modlocked && active)
|
||||||
|
balloon_alert(user, "it won't undeploy")
|
||||||
|
return FALSE
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/retract(mob/user, obj/item/part, instant)
|
||||||
|
if(!isprotean(user) && modlocked && active && !instant)
|
||||||
|
balloon_alert(user, "that button is unresponsive")
|
||||||
|
return FALSE
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/// Protean Revivial
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/tool_act(mob/living/user, obj/item/tool, list/modifiers)
|
||||||
|
. = ..()
|
||||||
|
var/obj/item/mod/core/protean/protean_core = core
|
||||||
|
var/obj/item/organ/brain/protean/brain = protean_core?.linked_species.owner.get_organ_slot(ORGAN_SLOT_BRAIN)
|
||||||
|
var/obj/item/organ/stomach/protean/refactory = protean_core.linked_species.owner.get_organ_slot(ORGAN_SLOT_STOMACH)
|
||||||
|
|
||||||
|
if(brain?.dead && open && istype(tool, /obj/item/organ/stomach/protean) && do_after(user, 10 SECONDS) && !refactory)
|
||||||
|
var/obj/item/organ/stomach = tool
|
||||||
|
stomach.Insert(protean_core.linked_species.owner, TRUE, DELETE_IF_REPLACED)
|
||||||
|
balloon_alert(user, "inserted!")
|
||||||
|
playsound(src, 'sound/machines/click.ogg', 50, TRUE, SILENCED_SOUND_EXTRARANGE)
|
||||||
|
brain.revive_timer()
|
||||||
|
return ITEM_INTERACT_SUCCESS
|
||||||
|
|
||||||
|
if(istype(tool, /obj/item/mod/control))
|
||||||
|
if(active)
|
||||||
|
balloon_alert(user, "turn it off")
|
||||||
|
return ITEM_INTERACT_BLOCKING
|
||||||
|
|
||||||
|
var/static/list/obj/item/mod/control/banned_modsuits = list(
|
||||||
|
/obj/item/mod/control/pre_equipped/infiltrator,
|
||||||
|
/obj/item/mod/control/pre_equipped/protean,)
|
||||||
|
|
||||||
|
if(is_type_in_list(tool, banned_modsuits))
|
||||||
|
balloon_alert(user, "incompatable")
|
||||||
|
return ITEM_INTERACT_BLOCKING
|
||||||
|
|
||||||
|
to_chat(user, span_notice("The suit begins to slowly absorb [tool]!"))
|
||||||
|
if(!do_after(user, 4 SECONDS))
|
||||||
|
return ITEM_INTERACT_BLOCKING
|
||||||
|
assimilate_modsuit(user, tool)
|
||||||
|
return ITEM_INTERACT_SUCCESS
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/ui_status(mob/user, datum/ui_state/state)
|
||||||
|
var/obj/item/mod/core/protean/source = core
|
||||||
|
var/datum/species/protean/species = source.linked_species
|
||||||
|
if(isprotean(species.owner) && species.owner == user && user.loc == src)
|
||||||
|
return 2
|
||||||
|
. = ..()
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/proc/assimilate_modsuit(mob/user, modsuit, forced)
|
||||||
|
var/obj/item/mod/control/to_assimilate = modsuit
|
||||||
|
if(stored_modsuit)
|
||||||
|
to_chat(user, span_warning("Can't absorb two modsuits!"))
|
||||||
|
if(forced)
|
||||||
|
stack_trace("assimilate_modsuit: Tried to assimilate modsuit while there's already a stored modsuit. stored_modsuit: [stored_modsuit], new_modsuit: [to_assimilate]")
|
||||||
|
return
|
||||||
|
if(!user.transferItemToLoc(to_assimilate, src, forced))
|
||||||
|
balloon_alert(user, "stuck!")
|
||||||
|
return
|
||||||
|
if(!forced)
|
||||||
|
for(var/obj/item/part as anything in get_parts())
|
||||||
|
if(part.loc == src)
|
||||||
|
continue
|
||||||
|
retract(null, part, instant = TRUE)
|
||||||
|
stored_modsuit = to_assimilate
|
||||||
|
stored_theme = theme // Store the old theme in cache
|
||||||
|
theme = to_assimilate.theme // Set new theme
|
||||||
|
complexity_max = to_assimilate.complexity_max // Inheret complexity
|
||||||
|
skin = to_assimilate.skin // Inheret skin
|
||||||
|
theme.set_up_parts(src, skin) // Put everything together
|
||||||
|
name = to_assimilate.name
|
||||||
|
desc = to_assimilate.desc
|
||||||
|
extended_desc = to_assimilate.extended_desc
|
||||||
|
for(var/obj/item/mod/module/module in to_assimilate.modules) // Insert every module
|
||||||
|
if(install(module, user, TRUE))
|
||||||
|
continue
|
||||||
|
uninstall(module) // Drop it if failed
|
||||||
|
update_static_data_for_all_viewers()
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/proc/unassimilate_modsuit(mob/living/user, forced = FALSE)
|
||||||
|
if(active && !forced)
|
||||||
|
balloon_alert(user, "deactivate modsuit")
|
||||||
|
return
|
||||||
|
if(!(user.has_active_hand()) && !forced)
|
||||||
|
balloon_alert(user, "need active hand")
|
||||||
|
return
|
||||||
|
|
||||||
|
if(!forced)
|
||||||
|
to_chat(user, span_notice("You begin to pry the assimilated modsuit away."))
|
||||||
|
if(!do_after(user, 4 SECONDS))
|
||||||
|
return
|
||||||
|
|
||||||
|
for(var/obj/item/part as anything in get_parts())
|
||||||
|
if(part.loc == src)
|
||||||
|
continue
|
||||||
|
retract(null, part, instant = TRUE)
|
||||||
|
|
||||||
|
complexity_max = initial(complexity_max)
|
||||||
|
for(var/obj/item/mod/module in modules) // Transfer back every module
|
||||||
|
if(stored_modsuit.install(module, user, TRUE))
|
||||||
|
continue
|
||||||
|
uninstall(module)
|
||||||
|
|
||||||
|
theme = stored_theme
|
||||||
|
stored_theme = null
|
||||||
|
skin = initial(skin)
|
||||||
|
theme.set_up_parts(src, skin)
|
||||||
|
name = initial(name)
|
||||||
|
desc = initial(desc)
|
||||||
|
extended_desc = initial(extended_desc)
|
||||||
|
//if(forced)
|
||||||
|
// stored_modsuit.forceMove(get_turf(src))
|
||||||
|
// stored_modsuit = null
|
||||||
|
if(user.can_put_in_hand(stored_modsuit, user.active_hand_index))
|
||||||
|
user.put_in_hand(stored_modsuit, user.active_hand_index)
|
||||||
|
stored_modsuit = null
|
||||||
|
update_static_data_for_all_viewers()
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/verb/remove_modsuit()
|
||||||
|
set name = "Remove Assimilated Modsuit"
|
||||||
|
|
||||||
|
unassimilate_modsuit(usr)
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/examine(mob/user)
|
||||||
|
. = ..()
|
||||||
|
var/obj/item/mod/core/protean/protean_core = core
|
||||||
|
var/obj/item/organ/brain/protean/brain = protean_core?.linked_species.owner.get_organ_slot(ORGAN_SLOT_BRAIN)
|
||||||
|
var/obj/item/organ/stomach/protean/refactory = protean_core.linked_species.owner.get_organ_slot(ORGAN_SLOT_STOMACH)
|
||||||
|
if(!isnull(brain) || istype(brain))
|
||||||
|
. += span_notice("<b>Control Shift Click</b> to open Protean strip menu.")
|
||||||
|
if(brain.dead)
|
||||||
|
if(!open)
|
||||||
|
. += isnull(refactory) ? span_warning("This Protean requires critical repairs! <b>Screwdriver them open</b>") : span_notice("<b>Repairing systems...</b>")
|
||||||
|
else
|
||||||
|
. += isnull(refactory) ? span_warning("<b>Insert a new refactory</b>") : span_notice("<b>Refactory Installed! Repairing systems...</b>")
|
||||||
|
|
||||||
|
/obj/item/mod/control/pre_equipped/protean/proc/ooc_escape(mob/living/carbon/user)
|
||||||
|
SIGNAL_HANDLER
|
||||||
|
|
||||||
|
if(isprotean(wearer))
|
||||||
|
return
|
||||||
|
drop_suit()
|
||||||
|
if(modlocked)
|
||||||
|
toggle_lock(TRUE)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protean stripping while they're in the suit.
|
||||||
|
* Yeah I guess stripping is an element. Carry on, citizen.
|
||||||
|
*/
|
||||||
|
/datum/element/strippable/protean
|
||||||
|
|
||||||
|
/datum/element/strippable/protean/Attach(datum/target, list/items, should_strip_proc_path)
|
||||||
|
. = ..()
|
||||||
|
RegisterSignal(target, COMSIG_CLICK_CTRL_SHIFT, PROC_REF(click_control_shit))
|
||||||
|
|
||||||
|
/datum/element/strippable/protean/Detach(datum/source)
|
||||||
|
. = ..()
|
||||||
|
UnregisterSignal(source, COMSIG_CLICK_CTRL_SHIFT)
|
||||||
|
|
||||||
|
/datum/element/strippable/protean/proc/click_control_shit(datum/source, mob/user)
|
||||||
|
SIGNAL_HANDLER
|
||||||
|
|
||||||
|
var/obj/item/mod/control/pre_equipped/protean/suit = source
|
||||||
|
if(!istype(suit))
|
||||||
|
return
|
||||||
|
var/obj/item/mod/core/protean/core = suit.core
|
||||||
|
var/datum/species/protean/species = core.linked_species
|
||||||
|
if(species.owner == user)
|
||||||
|
return
|
||||||
|
if(suit.wearer == source)
|
||||||
|
return
|
||||||
|
if (!isnull(should_strip_proc_path) && !call(species.owner, should_strip_proc_path)(user))
|
||||||
|
return
|
||||||
|
suit.balloon_alert_to_viewers("stripping")
|
||||||
|
ASYNC
|
||||||
|
var/datum/strip_menu/protean/strip_menu = LAZYACCESS(strip_menus, species.owner)
|
||||||
|
if (isnull(strip_menu))
|
||||||
|
strip_menu = new(species.owner, src)
|
||||||
|
LAZYSET(strip_menus, species.owner, strip_menu)
|
||||||
|
strip_menu.ui_interact(user)
|
||||||
|
|
||||||
|
/datum/strip_menu/protean
|
||||||
|
|
||||||
|
/datum/strip_menu/protean/ui_status(mob/user, datum/ui_state/state) // Needs to pass a viewcheck.
|
||||||
|
return min(
|
||||||
|
ui_status_only_living(user, owner),
|
||||||
|
ui_status_user_has_free_hands(user, owner),
|
||||||
|
ui_status_user_is_adjacent(user, owner, allow_tk = FALSE, viewcheck = FALSE),
|
||||||
|
HAS_TRAIT(user, TRAIT_CAN_STRIP) ? UI_INTERACTIVE : UI_UPDATE,
|
||||||
|
max(
|
||||||
|
ui_status_user_is_conscious_and_lying_down(user),
|
||||||
|
ui_status_user_is_abled(user, owner),
|
||||||
|
),
|
||||||
|
)
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/obj/item/mod/core/protean
|
||||||
|
name = "MOD protean core"
|
||||||
|
icon_state = "mod-core-ethereal"
|
||||||
|
desc = "If you see this, go scream at a coder and tell them how you managed to do this."
|
||||||
|
|
||||||
|
/// We handle as many interactions as possible through the species datum
|
||||||
|
/// The species handles cleanup on this
|
||||||
|
var/datum/species/protean/linked_species
|
||||||
|
|
||||||
|
/obj/item/mod/core/protean/charge_source()
|
||||||
|
if(isnull(linked_species))
|
||||||
|
return
|
||||||
|
if(isnull(linked_species.owner))
|
||||||
|
return
|
||||||
|
return linked_species.owner.get_organ_slot(ORGAN_SLOT_STOMACH)
|
||||||
|
|
||||||
|
/obj/item/mod/core/protean/charge_amount()
|
||||||
|
var/obj/item/organ/stomach/protean/stomach = charge_source()
|
||||||
|
var/obj/item/organ/brain/protean/brain = linked_species.owner.get_organ_slot(ORGAN_SLOT_BRAIN)
|
||||||
|
if(!istype(stomach))
|
||||||
|
return null
|
||||||
|
if(brain.dead)
|
||||||
|
return null
|
||||||
|
return stomach.metal
|
||||||
|
|
||||||
|
/obj/item/mod/core/protean/max_charge_amount()
|
||||||
|
return PROTEAN_STOMACH_FULL
|
||||||
|
|
||||||
|
/// We don't charge in a standard way
|
||||||
|
/obj/item/mod/core/protean/add_charge(amount)
|
||||||
|
return FALSE
|
||||||
|
|
||||||
|
/obj/item/mod/core/protean/subtract_charge(amount)
|
||||||
|
return FALSE
|
||||||
|
|
||||||
|
/obj/item/mod/core/protean/check_charge(amount)
|
||||||
|
var/obj/item/organ/stomach/protean/stomach = charge_source()
|
||||||
|
var/obj/item/organ/brain/protean/brain = linked_species.owner.get_organ_slot(ORGAN_SLOT_BRAIN)
|
||||||
|
if(stomach.metal <= PROTEAN_STOMACH_FALTERING)
|
||||||
|
return FALSE
|
||||||
|
if(!istype(brain) || brain.dead)
|
||||||
|
return FALSE
|
||||||
|
return TRUE
|
||||||
|
|
||||||
|
/obj/item/mod/core/protean/get_charge_icon_state()
|
||||||
|
return charge_source() ? "0" : "missing"
|
||||||
|
|
||||||
|
/obj/item/mod/core/protean/get_chargebar_color()
|
||||||
|
if(isnull(charge_amount()))
|
||||||
|
return "transparent"
|
||||||
|
switch(charge_amount())
|
||||||
|
if (-INFINITY to (PROTEAN_STOMACH_FULL * 0.3))
|
||||||
|
return "bad"
|
||||||
|
if((PROTEAN_STOMACH_FULL * 0.3) to (PROTEAN_STOMACH_FULL * 0.7))
|
||||||
|
return "average"
|
||||||
|
if((PROTEAN_STOMACH_FULL * 0.7) to INFINITY)
|
||||||
|
return "good"
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/obj/item/organ/heart/protean
|
||||||
|
name = "orchestrator module"
|
||||||
|
desc = "A small computer, designed for highly parallel workloads."
|
||||||
|
icon = PROTEAN_ORGAN_SPRITE
|
||||||
|
icon_state = "orchestrator"
|
||||||
|
organ_flags = ORGAN_ROBOTIC | ORGAN_NANOMACHINE
|
||||||
|
|
||||||
|
/obj/item/organ/eyes/robotic/protean
|
||||||
|
name = "imaging nanites"
|
||||||
|
desc = "Nanites designed to collect visual data from the surrounding world"
|
||||||
|
organ_flags = ORGAN_ROBOTIC
|
||||||
|
flash_protect = FLASH_PROTECTION_WELDER
|
||||||
|
|
||||||
|
/obj/item/organ/eyes/robotic/protean/Initialize(mapload)
|
||||||
|
if(QDELETED(src))
|
||||||
|
return FALSE
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/obj/item/organ/eyes/robotic/protean/Initialize(mapload)
|
||||||
|
. = ..()
|
||||||
|
AddElement(/datum/element/nanite_organ)
|
||||||
|
|
||||||
|
/obj/item/organ/ears/cybernetic/protean
|
||||||
|
name = "sensory nanites"
|
||||||
|
desc = "Nanites designed to collect audio feedback from the surrounding world"
|
||||||
|
organ_flags = ORGAN_ROBOTIC
|
||||||
|
|
||||||
|
/obj/item/organ/ears/cybernetic/protean/Insert(mob/living/carbon/receiver, special, movement_flags)
|
||||||
|
if(QDELETED(src))
|
||||||
|
return FALSE
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/obj/item/organ/ears/cybernetic/protean/Initialize(mapload)
|
||||||
|
. = ..()
|
||||||
|
AddElement(/datum/element/nanite_organ)
|
||||||
|
|
||||||
|
/obj/item/organ/tongue/cybernetic/protean
|
||||||
|
name = "protean audio fabricator"
|
||||||
|
desc = "Millions of nanites vibrate in harmony to create the sound you hear."
|
||||||
|
organ_flags = ORGAN_ROBOTIC
|
||||||
|
|
||||||
|
/obj/item/organ/tongue/cybernetic/protean/Insert(mob/living/carbon/receiver, special, movement_flags)
|
||||||
|
if(QDELETED(src))
|
||||||
|
return FALSE
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/obj/item/organ/tongue/cybernetic/protean/Initialize(mapload)
|
||||||
|
. = ..()
|
||||||
|
AddElement(/datum/element/nanite_organ)
|
||||||
|
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/obj/item/organ/stomach/protean
|
||||||
|
name = "refactory"
|
||||||
|
desc = "An extremely fragile factory used to rescyle materials and create more nanite mass"
|
||||||
|
icon = PROTEAN_ORGAN_SPRITE
|
||||||
|
icon_state = "refactory"
|
||||||
|
organ_flags = ORGAN_ROBOTIC | ORGAN_NANOMACHINE
|
||||||
|
organ_traits = list(TRAIT_NOHUNGER)
|
||||||
|
|
||||||
|
/// How much max metal can we hold at any given time (In sheets). This isn't using nutrition code because nutrition code gets weird without livers.
|
||||||
|
var/metal_max = PROTEAN_STOMACH_FULL
|
||||||
|
/// How much metal are we holding currently (In sheets)
|
||||||
|
var/metal = PROTEAN_STOMACH_FULL
|
||||||
|
COOLDOWN_DECLARE(starving_message)
|
||||||
|
|
||||||
|
/obj/item/organ/stomach/protean/Initialize(mapload)
|
||||||
|
. = ..() // Call the rest of the proc
|
||||||
|
metal = round(rand(PROTEAN_STOMACH_FULL/2, PROTEAN_STOMACH_FULL))
|
||||||
|
|
||||||
|
/obj/item/organ/stomach/protean/on_mob_insert(mob/living/carbon/receiver, special, movement_flags)
|
||||||
|
. = ..()
|
||||||
|
RegisterSignal(receiver, COMSIG_CARBON_ATTEMPT_EAT, PROC_REF(try_stomach_eat))
|
||||||
|
|
||||||
|
/obj/item/organ/stomach/protean/on_mob_remove(mob/living/carbon/stomach_owner, special, movement_flags)
|
||||||
|
. = ..()
|
||||||
|
UnregisterSignal(stomach_owner, COMSIG_CARBON_ATTEMPT_EAT)
|
||||||
|
|
||||||
|
/obj/item/organ/stomach/protean/on_life(seconds_per_tick, times_fired)
|
||||||
|
var/datum/species/protean/species = owner?.dna.species
|
||||||
|
var/obj/item/mod/control/pre_equipped/protean/suit = species.species_modsuit
|
||||||
|
if(owner.loc == suit)
|
||||||
|
return
|
||||||
|
/// Zero out any nutrition. We do not use hunger in this species.
|
||||||
|
for(var/datum/reagent/consumable/food in reagents.reagent_list)
|
||||||
|
food.nutriment_factor = 0
|
||||||
|
. = ..()
|
||||||
|
handle_hunger_slowdown(owner, seconds_per_tick)
|
||||||
|
|
||||||
|
/obj/item/organ/stomach/protean/handle_hunger_slowdown(mob/living/carbon/human/human, seconds_per_tick)
|
||||||
|
if(!istype(owner.dna.species, /datum/species/protean))
|
||||||
|
return
|
||||||
|
if(metal > PROTEAN_STOMACH_FALTERING)
|
||||||
|
owner.remove_movespeed_modifier(/datum/movespeed_modifier/protean_slowdown)
|
||||||
|
if(owner.health < owner.maxHealth && metal > PROTEAN_STOMACH_FULL * 0.3)
|
||||||
|
metal -= clamp(((PROTEAN_STOMACH_FULL / PROTEAN_METABOLISM_RATE) * seconds_per_tick * 20), 0, 10) // Healing needs metal. 0.2 sheets per tick
|
||||||
|
owner.adjustBruteLoss(-2, forced = TRUE)
|
||||||
|
owner.adjustFireLoss(-2, forced = TRUE)
|
||||||
|
else
|
||||||
|
metal -= clamp(((PROTEAN_STOMACH_FULL / PROTEAN_METABOLISM_RATE) * seconds_per_tick), 0, 10)
|
||||||
|
return
|
||||||
|
owner.adjustBruteLoss(2, forced = TRUE)
|
||||||
|
if(COOLDOWN_FINISHED(src, starving_message))
|
||||||
|
to_chat(owner, span_warning("You are starving! You must find metal now!"))
|
||||||
|
owner.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/protean_slowdown, multiplicative_slowdown = 2)
|
||||||
|
COOLDOWN_START(src, starving_message, 20 SECONDS)
|
||||||
|
|
||||||
|
/// Check to see if our metal storage is full.
|
||||||
|
/obj/item/organ/stomach/protean/proc/try_stomach_eat(mob/eater, atom/eating)
|
||||||
|
SIGNAL_HANDLER
|
||||||
|
|
||||||
|
if(istype(eating, /obj/item/food/golem_food))
|
||||||
|
var/obj/item/food/golem_food/food = eating
|
||||||
|
if(metal > (PROTEAN_STOMACH_FULL - 0.3) && food.owner.loc == owner)
|
||||||
|
balloon_alert(owner, "storage full!")
|
||||||
|
return COMSIG_CARBON_BLOCK_EAT
|
||||||
|
|
||||||
|
/// If we ate a sheet of metal, add it to storage.
|
||||||
|
/obj/item/organ/stomach/protean/after_eat(atom/edible)
|
||||||
|
if(istype(edible, /obj/item/food/golem_food))
|
||||||
|
var/obj/item/food/golem_food/food = edible
|
||||||
|
metal = clamp(metal + 1, 0, PROTEAN_STOMACH_FULL)
|
||||||
|
if(food.owner.loc != owner) // Other people feeding them will heal them.
|
||||||
|
owner.adjustBruteLoss(-20, forced = TRUE)
|
||||||
|
var/health_check = owner.health >= owner.maxHealth ? "fully healed!" : "healed!"
|
||||||
|
owner.balloon_alert_to_viewers("[health_check]")
|
||||||
|
|
||||||
|
#undef PROTEAN_STOMACH_FULL
|
||||||
|
#undef PROTEAN_STOMACH_FALTERING
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/mob/living/carbon/proc/protean_ui()
|
||||||
|
set name = "Open Suit UI"
|
||||||
|
set desc = "Opens your suit UI"
|
||||||
|
set category = "Protean"
|
||||||
|
|
||||||
|
var/datum/species/protean/species = dna.species
|
||||||
|
if(!istype(species))
|
||||||
|
return
|
||||||
|
species.species_modsuit.ui_interact(src)
|
||||||
|
|
||||||
|
/mob/living/carbon/proc/protean_heal()
|
||||||
|
set name = "Heal Organs and Limbs"
|
||||||
|
set desc = "Heals your replacable organs and limbs with 6 metal."
|
||||||
|
set category = "Protean"
|
||||||
|
|
||||||
|
var/obj/item/organ/brain/protean/brain = get_organ_slot(ORGAN_SLOT_BRAIN)
|
||||||
|
|
||||||
|
if(!istype(brain))
|
||||||
|
return
|
||||||
|
|
||||||
|
var/datum/species/protean/species = dna.species
|
||||||
|
var/obj/item/mod/control/pre_equipped/protean/suit = species.species_modsuit
|
||||||
|
if(incapacitated && loc != suit)
|
||||||
|
balloon_alert(src, "incapacitated!")
|
||||||
|
return
|
||||||
|
|
||||||
|
brain.replace_limbs()
|
||||||
|
|
||||||
|
/mob/living/carbon/proc/lock_suit()
|
||||||
|
set name = "Lock Suit"
|
||||||
|
set desc = "Locks your suit on someone"
|
||||||
|
set category = "Protean"
|
||||||
|
|
||||||
|
var/datum/species/protean/species = dna.species
|
||||||
|
|
||||||
|
if(!istype(species))
|
||||||
|
return
|
||||||
|
|
||||||
|
var/obj/item/mod/control/pre_equipped/protean/suit = species.species_modsuit
|
||||||
|
species.species_modsuit.toggle_lock()
|
||||||
|
to_chat(src, span_notice("You [suit.modlocked ? "<b>lock</b>" : "<b>unlock</b>"] the suit [isprotean(suit.wearer) || loc == suit ? "" : "onto [suit.wearer]"]"))
|
||||||
|
playsound(src, 'sound/machines/click.ogg', 25)
|
||||||
|
|
||||||
|
/mob/living/carbon/proc/suit_transformation()
|
||||||
|
set name = "Toggle Suit Transformation"
|
||||||
|
set desc = "Either leave or enter your suit."
|
||||||
|
set category = "Protean"
|
||||||
|
var/obj/item/organ/brain/protean/brain = get_organ_slot(ORGAN_SLOT_BRAIN)
|
||||||
|
|
||||||
|
if(!istype(brain))
|
||||||
|
return
|
||||||
|
var/datum/species/protean/species = dna.species
|
||||||
|
if(loc == species.species_modsuit)
|
||||||
|
brain.leave_modsuit()
|
||||||
|
else if(isturf(loc))
|
||||||
|
if(!incapacitated)
|
||||||
|
brain.go_into_suit()
|
||||||
|
else
|
||||||
|
balloon_alert(src, "incapacitated!")
|
||||||
|
|
||||||
@@ -39,3 +39,12 @@
|
|||||||
contains = list(/obj/item/firing_pin/alert_level = 4)
|
contains = list(/obj/item/firing_pin/alert_level = 4)
|
||||||
crate_name = "alert level firing pin crate"
|
crate_name = "alert level firing pin crate"
|
||||||
|
|
||||||
|
/datum/supply_pack/science/protean_organs
|
||||||
|
name = "Protean Organs"
|
||||||
|
desc = "Contains two sets of organs for Protean crewmembers."
|
||||||
|
cost = CARGO_CRATE_VALUE * 10 // Not cheap
|
||||||
|
contains = list(/obj/item/organ/stomach/protean = 2, /obj/item/organ/heart/protean = 2)
|
||||||
|
crate_name = "protean organs"
|
||||||
|
access = ACCESS_ROBOTICS
|
||||||
|
access_view = ACCESS_ROBOTICS
|
||||||
|
crate_type = /obj/structure/closet/crate/secure/science/robo
|
||||||
|
|||||||
@@ -34,7 +34,8 @@
|
|||||||
|
|
||||||
/// Restricted roles from the antag roll
|
/// Restricted roles from the antag roll
|
||||||
var/restricted_roles = list(JOB_AI, JOB_CYBORG)
|
var/restricted_roles = list(JOB_AI, JOB_CYBORG)
|
||||||
|
/// Restricted species from the antag roll
|
||||||
|
var/restricted_species = list()
|
||||||
/// How many baseline antags do we spawn
|
/// How many baseline antags do we spawn
|
||||||
var/base_antags = 1
|
var/base_antags = 1
|
||||||
/// How many maximum antags can we spawn
|
/// How many maximum antags can we spawn
|
||||||
@@ -81,7 +82,7 @@
|
|||||||
|
|
||||||
/datum/round_event_control/antagonist/proc/get_candidates()
|
/datum/round_event_control/antagonist/proc/get_candidates()
|
||||||
var/round_started = SSticker.HasRoundStarted()
|
var/round_started = SSticker.HasRoundStarted()
|
||||||
var/list/candidates = SSgamemode.get_candidates(antag_flag, pick_roundstart_players = !round_started, restricted_roles = restricted_roles)
|
var/list/candidates = SSgamemode.get_candidates(antag_flag, pick_roundstart_players = !round_started, restricted_roles = restricted_roles, restricted_species = restricted_species)
|
||||||
return candidates
|
return candidates
|
||||||
|
|
||||||
/datum/round_event_control/antagonist/solo
|
/datum/round_event_control/antagonist/solo
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
min_players = 30
|
min_players = 30
|
||||||
|
|
||||||
maximum_antags_global = 3
|
maximum_antags_global = 3
|
||||||
|
restricted_species = list(SPECIES_PROTEAN)
|
||||||
tags = list(TAG_COMBAT, TAG_SPOOKY, TAG_CREW_ANTAG)
|
tags = list(TAG_COMBAT, TAG_SPOOKY, TAG_CREW_ANTAG)
|
||||||
|
|
||||||
/datum/round_event_control/antagonist/solo/bloodsucker/midround
|
/datum/round_event_control/antagonist/solo/bloodsucker/midround
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
weight = 8
|
weight = 8
|
||||||
min_players = 20
|
min_players = 20
|
||||||
maximum_antags_global = 4
|
maximum_antags_global = 4
|
||||||
|
restricted_species = list(SPECIES_PROTEAN)
|
||||||
|
|
||||||
tags = list(TAG_COMBAT, TAG_CREW_ANTAG)
|
tags = list(TAG_COMBAT, TAG_CREW_ANTAG)
|
||||||
|
|
||||||
|
|||||||
@@ -251,6 +251,7 @@ SUBSYSTEM_DEF(gamemode)
|
|||||||
inherit_required_time = TRUE,
|
inherit_required_time = TRUE,
|
||||||
no_antags = TRUE,
|
no_antags = TRUE,
|
||||||
list/restricted_roles,
|
list/restricted_roles,
|
||||||
|
list/restricted_species,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -279,10 +280,13 @@ SUBSYSTEM_DEF(gamemode)
|
|||||||
continue
|
continue
|
||||||
if(restricted_roles && (candidate.mind.assigned_role.title in restricted_roles))
|
if(restricted_roles && (candidate.mind.assigned_role.title in restricted_roles))
|
||||||
continue
|
continue
|
||||||
|
var/mob/living/carbon/carbon = candidate
|
||||||
|
if(istype(carbon)) // species restrictions
|
||||||
|
if(restricted_species && (carbon.dna.species.id in restricted_species))
|
||||||
|
continue
|
||||||
if(special_role_flag)
|
if(special_role_flag)
|
||||||
if(!(candidate.client.prefs) || !(special_role_flag in candidate.client.prefs.be_special))
|
if(!(candidate.client.prefs) || !(special_role_flag in candidate.client.prefs.be_special))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
var/time_to_check
|
var/time_to_check
|
||||||
if(required_time)
|
if(required_time)
|
||||||
time_to_check = required_time
|
time_to_check = required_time
|
||||||
|
|||||||
BIN
modular_zubbers/icons/mob/species/protean/protean.dmi
Normal file
BIN
modular_zubbers/icons/mob/species/protean/protean.dmi
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
@@ -518,6 +518,7 @@
|
|||||||
#include "code\__DEFINES\~~bubber_defines\chat.dm"
|
#include "code\__DEFINES\~~bubber_defines\chat.dm"
|
||||||
#include "code\__DEFINES\~~bubber_defines\combat.dm"
|
#include "code\__DEFINES\~~bubber_defines\combat.dm"
|
||||||
#include "code\__DEFINES\~~bubber_defines\cooldowns.dm"
|
#include "code\__DEFINES\~~bubber_defines\cooldowns.dm"
|
||||||
|
#include "code\__DEFINES\~~bubber_defines\dna.dm"
|
||||||
#include "code\__DEFINES\~~bubber_defines\economy.dm"
|
#include "code\__DEFINES\~~bubber_defines\economy.dm"
|
||||||
#include "code\__DEFINES\~~bubber_defines\experisci.dm"
|
#include "code\__DEFINES\~~bubber_defines\experisci.dm"
|
||||||
#include "code\__DEFINES\~~bubber_defines\footsteps.dm"
|
#include "code\__DEFINES\~~bubber_defines\footsteps.dm"
|
||||||
@@ -8874,6 +8875,7 @@
|
|||||||
#include "modular_zubbers\code\datums\computer_datums\protolathe_modifications.dm"
|
#include "modular_zubbers\code\datums\computer_datums\protolathe_modifications.dm"
|
||||||
#include "modular_zubbers\code\datums\diseases\advance\presets.dm"
|
#include "modular_zubbers\code\datums\diseases\advance\presets.dm"
|
||||||
#include "modular_zubbers\code\datums\elements\examine_when_worn.dm"
|
#include "modular_zubbers\code\datums\elements\examine_when_worn.dm"
|
||||||
|
#include "modular_zubbers\code\datums\elements\nanite_organ.dm"
|
||||||
#include "modular_zubbers\code\datums\greyscale\config_types\greyscale_configs\greyscale_clothes.dm"
|
#include "modular_zubbers\code\datums\greyscale\config_types\greyscale_configs\greyscale_clothes.dm"
|
||||||
#include "modular_zubbers\code\datums\greyscale\config_types\greyscale_configs\pipe_carp.dm"
|
#include "modular_zubbers\code\datums\greyscale\config_types\greyscale_configs\pipe_carp.dm"
|
||||||
#include "modular_zubbers\code\datums\mapgen\Cavegens\moonstation.dm"
|
#include "modular_zubbers\code\datums\mapgen\Cavegens\moonstation.dm"
|
||||||
@@ -9224,6 +9226,14 @@
|
|||||||
#include "modular_zubbers\code\modules\credits\credits.dm"
|
#include "modular_zubbers\code\modules\credits\credits.dm"
|
||||||
#include "modular_zubbers\code\modules\customization\height_scaling\icons.dm"
|
#include "modular_zubbers\code\modules\customization\height_scaling\icons.dm"
|
||||||
#include "modular_zubbers\code\modules\customization\height_scaling\preferences.dm"
|
#include "modular_zubbers\code\modules\customization\height_scaling\preferences.dm"
|
||||||
|
#include "modular_zubbers\code\modules\customization\species\proteans\__DEFINES.dm"
|
||||||
|
#include "modular_zubbers\code\modules\customization\species\proteans\_proteans_species.dm"
|
||||||
|
#include "modular_zubbers\code\modules\customization\species\proteans\protean_brain.dm"
|
||||||
|
#include "modular_zubbers\code\modules\customization\species\proteans\protean_modsuit.dm"
|
||||||
|
#include "modular_zubbers\code\modules\customization\species\proteans\protean_modsuit_core.dm"
|
||||||
|
#include "modular_zubbers\code\modules\customization\species\proteans\protean_organs.dm"
|
||||||
|
#include "modular_zubbers\code\modules\customization\species\proteans\protean_stomach.dm"
|
||||||
|
#include "modular_zubbers\code\modules\customization\species\proteans\protean_verbs.dm"
|
||||||
#include "modular_zubbers\code\modules\customization\species\synths\death_sound.dm"
|
#include "modular_zubbers\code\modules\customization\species\synths\death_sound.dm"
|
||||||
#include "modular_zubbers\code\modules\customization\species\synths\code\synth_prefab.dm"
|
#include "modular_zubbers\code\modules\customization\species\synths\code\synth_prefab.dm"
|
||||||
#include "modular_zubbers\code\modules\customization\sprite_accessories\64_wings.dm"
|
#include "modular_zubbers\code\modules\customization\sprite_accessories\64_wings.dm"
|
||||||
|
|||||||
Reference in New Issue
Block a user