From 4b4ada033920341b39d5c2adf334946daedaa7b9 Mon Sep 17 00:00:00 2001 From: Profakos Date: Sat, 25 Nov 2023 19:10:55 +0100 Subject: [PATCH] Slime type datum, and cleans up slime related code (#79852) ## About The Pull Request I have decided to help out with basic mobbing slimes, and found out, that this code is ancient and crusty. So I have decided to refactor and tweak things bit but but, until the moment I will hit AI related stuff. I might have gone a bit overboard, and performed a melange of fixes. I have done the following in order: - Slime colour, core type and mutation list are now held by a single `slime_type datum`. This means the slime's core is no longer spawned using text operations on the slime's colour, and the slime's mutation is no longer a single large switch statement that returns an exactly four element length array. Icons are still based on the slime's colour define, but that is now more changeable. - Autodocced all the slime procs and vars I could find, while also renamed them to be more descriptive. Also made Booleans actual booleans. Also added code to confused code segments. For example, did you know that slimes would forgive all its stacks of Feeling Very Attack Right Now, if it has exactly one level of Discipline? I didn't, and I almost turned the 1 in the check into a TRUE before I had a second look. - Added defines to the slime hunger checks - Cleaned up a lot of single letter vars in all code that referenced the now changed variables. - Large amount of if-chain pyramids have been turned into early returns. More readable. - Made the xenobiology camera shortcuts and actions always do the same thing, though the action buttons still loop through all of the conents. In addition, after seeing a comment lamenting about varied load orders, I have made autolinking the monkey recycler is now done in LateInitialize, which didn't exist when it was made. - I have included an UpdatePath that changes the colours of premapped slimes, but I did not do anything for the other values. - I noticed slimes were unable to attack pacifist humans, due to trait checking the human instead of the attacking slime. This has been fixed. - The slime scanner has an examine block, and it now describes your slimes' "mutation attempt chance", instead of like, performing math in case the slime's current colour is one of the mutations, or hiding it if it has none. This value is needed to breed rainbow slimes. This has been an extremely large scrubbing. I am willing to try rip it up into smaller chunks, even if most of it is just single letter vars and if pyramids. ## Why It's Good For The Game If code is more readable and expandable, it will aid in further refactors. ## Changelog :cl: refactor: Slimes's colour, core type and mutation list is now held in a slime type datum code: Slime's variables have been documented, and renamed a bit to add clarity. Please report bugs that might stem from renaming. fix: Slimes are not longer prevented from attacking pacifist humans. qol: Slime scans now display the actual amount of genetic instability, instead of hiding it if a slime doesn't mutate further, or tweaking it if it might mutate back into itself. This will make it easier to parse which slime to breed further to get a rainbow slime. /:cl: --- .../SpaceRuins/hilbertresearchfacility.dmm | 16 +- code/game/machinery/syndicatebomb.dm | 2 +- .../anomalies/anomalies_pyroclastic.dm | 2 +- .../items/devices/scanners/slime_scanner.dm | 61 +-- .../objects/items/grenades/chem_grenade.dm | 2 +- .../abductor/equipment/glands/slime.dm | 2 +- .../food_and_drinks/machinery/processor.dm | 4 +- .../carbon/human/species_types/jellypeople.dm | 2 +- code/modules/mob/living/living_defense.dm | 24 +- .../mob/living/simple_animal/slime/death.dm | 16 +- .../mob/living/simple_animal/slime/life.dm | 355 +++++++------- .../mob/living/simple_animal/slime/powers.dm | 74 +-- .../mob/living/simple_animal/slime/slime.dm | 386 +++++++-------- .../living/simple_animal/slime/slime_type.dm | 224 +++++++++ .../living/simple_animal/slime/subtypes.dm | 79 --- .../mob_spawn/corpses/nonhuman_corpses.dm | 4 +- .../reagents/chemistry/holder/reactions.dm | 4 +- .../chemistry/recipes/slime_extracts.dm | 16 +- .../xenobiology/crossbreeding/_potions.dm | 2 +- .../crossbreeding/_status_effects.dm | 8 +- .../xenobiology/crossbreeding/burning.dm | 26 +- .../xenobiology/crossbreeding/charged.dm | 4 +- .../xenobiology/crossbreeding/chilling.dm | 4 +- .../xenobiology/crossbreeding/recurring.dm | 6 +- .../xenobiology/crossbreeding/regenerative.dm | 4 +- .../research/xenobiology/xenobio_camera.dm | 456 +++++++++--------- .../research/xenobiology/xenobiology.dm | 96 ++-- code/modules/surgery/core_removal.dm | 4 +- tgstation.dme | 2 +- .../Scripts/79852_slime_colour_renames.txt | 22 + 30 files changed, 1058 insertions(+), 849 deletions(-) create mode 100644 code/modules/mob/living/simple_animal/slime/slime_type.dm delete mode 100644 code/modules/mob/living/simple_animal/slime/subtypes.dm create mode 100644 tools/UpdatePaths/Scripts/79852_slime_colour_renames.txt diff --git a/_maps/RandomRuins/SpaceRuins/hilbertresearchfacility.dmm b/_maps/RandomRuins/SpaceRuins/hilbertresearchfacility.dmm index 820a5720dc4..b24f91fd4ab 100644 --- a/_maps/RandomRuins/SpaceRuins/hilbertresearchfacility.dmm +++ b/_maps/RandomRuins/SpaceRuins/hilbertresearchfacility.dmm @@ -356,8 +356,8 @@ /area/ruin/space/has_grav/powered/hilbertresearchfacility) "jC" = ( /mob/living/simple_animal/slime{ - colour = "bluespace"; - rabid = 1 + rabid = 1; + slime_type = /datum/slime_type/bluespace }, /obj/effect/decal/cleanable/dirt, /turf/open/floor/mineral/titanium/tiled/purple, @@ -899,8 +899,8 @@ /area/ruin/space/has_grav/powered/hilbertresearchfacility) "wY" = ( /mob/living/simple_animal/slime{ - colour = "bluespace"; - rabid = 1 + rabid = 1; + slime_type = /datum/slime_type/bluespace }, /turf/open/floor/mineral/plastitanium, /area/ruin/space/has_grav/powered/hilbertresearchfacility) @@ -2186,8 +2186,8 @@ }, /obj/effect/decal/cleanable/dirt, /mob/living/simple_animal/slime{ - colour = "bluespace"; - rabid = 1 + rabid = 1; + slime_type = /datum/slime_type/bluespace }, /turf/open/floor/mineral/titanium/tiled/purple, /area/ruin/space/has_grav/powered/hilbertresearchfacility) @@ -2312,8 +2312,8 @@ /area/ruin/space/has_grav/powered/hilbertresearchfacility) "Ze" = ( /mob/living/simple_animal/slime{ - colour = "bluespace"; - rabid = 1 + rabid = 1; + slime_type = /datum/slime_type/bluespace }, /obj/effect/decal/cleanable/dirt, /turf/open/floor/mineral/plastitanium, diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm index acd01f2488c..d14d519efb1 100644 --- a/code/game/machinery/syndicatebomb.dm +++ b/code/game/machinery/syndicatebomb.dm @@ -494,7 +494,7 @@ reactants += G.reagents for(var/obj/item/slime_extract/S in beakers) - if(S.Uses) + if(S.extract_uses) for(var/obj/item/reagent_containers/cup/G in beakers) G.reagents.trans_to(S, G.reagents.total_volume) diff --git a/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm b/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm index b290c23ac3f..d3ee84181ed 100644 --- a/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm +++ b/code/game/objects/effects/anomalies/anomalies_pyroclastic.dm @@ -31,7 +31,7 @@ if(istype(tile)) tile.atmos_spawn_air("[GAS_O2]=500;[GAS_PLASMA]=500;[TURF_TEMPERATURE(1000)]") //Make it hot and burny for the new slime - var/new_colour = pick("red", "orange") + var/new_colour = pick(/datum/slime_type/red, /datum/slime_type/orange) var/mob/living/simple_animal/slime/pyro = new(tile, new_colour) pyro.rabid = TRUE pyro.amount_grown = SLIME_EVOLUTION_THRESHOLD diff --git a/code/game/objects/items/devices/scanners/slime_scanner.dm b/code/game/objects/items/devices/scanners/slime_scanner.dm index 430796e02ca..6d2c6311229 100644 --- a/code/game/objects/items/devices/scanners/slime_scanner.dm +++ b/code/game/objects/items/devices/scanners/slime_scanner.dm @@ -13,41 +13,44 @@ throw_range = 7 custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*0.30, /datum/material/glass=SMALL_MATERIAL_AMOUNT * 0.20) -/obj/item/slime_scanner/attack(mob/living/M, mob/living/user) +/obj/item/slime_scanner/attack(mob/living/living_mob, mob/living/user) if(user.stat || !user.can_read(src) || user.is_blind()) return - if (!isslime(M)) + if (!isslime(living_mob)) to_chat(user, span_warning("This device can only scan slimes!")) return - var/mob/living/simple_animal/slime/T = M - slime_scan(T, user) + var/mob/living/simple_animal/slime/scanned_slime = living_mob + slime_scan(scanned_slime, user) -/proc/slime_scan(mob/living/simple_animal/slime/T, mob/living/user) +/proc/slime_scan(mob/living/simple_animal/slime/scanned_slime, mob/living/user) var/to_render = "Slime scan results:\ - \n[span_notice("[T.colour] [T.is_adult ? "adult" : "baby"] slime")]\ - \nNutrition: [T.nutrition]/[T.get_max_nutrition()]" - if (T.nutrition < T.get_starve_nutrition()) + \n[span_notice("[scanned_slime.slime_type.colour] [scanned_slime.is_adult ? "adult" : "baby"] slime")]\ + \nNutrition: [scanned_slime.nutrition]/[scanned_slime.get_max_nutrition()]" + + if (scanned_slime.nutrition < scanned_slime.get_starve_nutrition()) to_render += "\n[span_warning("Warning: slime is starving!")]" - else if (T.nutrition < T.get_hunger_nutrition()) + else if (scanned_slime.nutrition < scanned_slime.get_hunger_nutrition()) to_render += "\n[span_warning("Warning: slime is hungry")]" - to_render += "\nElectric change strength: [T.powerlevel]\nHealth: [round(T.health/T.maxHealth,0.01)*100]%" - if (T.slime_mutation[4] == T.colour) - to_render += "\nThis slime does not evolve any further." - else - if (T.slime_mutation[3] == T.slime_mutation[4]) - if (T.slime_mutation[2] == T.slime_mutation[1]) - to_render += "\nPossible mutation: [T.slime_mutation[3]]\ - \nGenetic destability: [T.mutation_chance/2] % chance of mutation on splitting" - else - to_render += "\nPossible mutations: [T.slime_mutation[1]], [T.slime_mutation[2]], [T.slime_mutation[3]] (x2)\ - \nGenetic destability: [T.mutation_chance] % chance of mutation on splitting" - else - to_render += "\nPossible mutations: [T.slime_mutation[1]], [T.slime_mutation[2]], [T.slime_mutation[3]], [T.slime_mutation[4]]\ - \nGenetic destability: [T.mutation_chance] % chance of mutation on splitting" - if (T.cores > 1) + + to_render += "\nElectric charge strength: [scanned_slime.powerlevel]\nHealth: [round(scanned_slime.health/scanned_slime.maxHealth,0.01)*100]%" + + to_render += "\nPossible mutation[scanned_slime.slime_type.mutations.len > 1 ? "s" : ""]: " + var/list/mutation_text = list() + for(var/datum/slime_type/key as anything in scanned_slime.slime_type.mutations) + mutation_text += initial(key.colour) + + if(!mutation_text.len) + to_render += " None detected." + + to_render += "[mutation_text.Join(", ")]" + to_render += "\nGenetic instability: [scanned_slime.mutation_chance] % chance of mutation attempt on splitting." + + if (scanned_slime.cores > 1) to_render += "\nMultiple cores detected" - to_render += "\nGrowth progress: [T.amount_grown]/[SLIME_EVOLUTION_THRESHOLD]" - if(T.effectmod) - to_render += "\n[span_notice("Core mutation in progress: [T.effectmod]")]\ - \n[span_notice("Progress in core mutation: [T.applied] / [SLIME_EXTRACT_CROSSING_REQUIRED]")]" - to_chat(user, examine_block(to_render)) + to_render += "\nGrowth progress: [scanned_slime.amount_grown]/[SLIME_EVOLUTION_THRESHOLD]" + + if(scanned_slime.crossbreed_modification) + to_render += "\n[span_notice("Core mutation in progress: [scanned_slime.crossbreed_modification]")]\ + \n[span_notice("Progress in core mutation: [scanned_slime.applied_crossbreed_amount] / [SLIME_EXTRACT_CROSSING_REQUIRED]")]" + + to_chat(user, examine_block(jointext(to_render,""))) diff --git a/code/game/objects/items/grenades/chem_grenade.dm b/code/game/objects/items/grenades/chem_grenade.dm index f8ac3748de9..2c01ad53ede 100644 --- a/code/game/objects/items/grenades/chem_grenade.dm +++ b/code/game/objects/items/grenades/chem_grenade.dm @@ -295,7 +295,7 @@ if(istype(thing, /obj/item/slime_extract)) var/obj/item/slime_extract/extract = thing - if(!extract.Uses) + if(!extract.extract_uses) continue extract_total_volume += extract.reagents.total_volume diff --git a/code/modules/antagonists/abductor/equipment/glands/slime.dm b/code/modules/antagonists/abductor/equipment/glands/slime.dm index faebce9fc87..60e680bc2a9 100644 --- a/code/modules/antagonists/abductor/equipment/glands/slime.dm +++ b/code/modules/antagonists/abductor/equipment/glands/slime.dm @@ -21,6 +21,6 @@ to_chat(owner, span_warning("You feel nauseated!")) owner.vomit(VOMIT_CATEGORY_DEFAULT, lost_nutrition = 20) - var/mob/living/simple_animal/slime/Slime = new(get_turf(owner), "grey") + var/mob/living/simple_animal/slime/Slime = new(get_turf(owner), /datum/slime_type/grey) Slime.set_friends(list(owner)) Slime.set_leader(owner) diff --git a/code/modules/food_and_drinks/machinery/processor.dm b/code/modules/food_and_drinks/machinery/processor.dm index 6a9d6090c1f..c10d0b8c639 100644 --- a/code/modules/food_and_drinks/machinery/processor.dm +++ b/code/modules/food_and_drinks/machinery/processor.dm @@ -236,9 +236,9 @@ return var/core_count = processed_slime.cores for(var/i in 1 to (core_count+rating_amount-1)) - var/atom/movable/item = new processed_slime.coretype(drop_location()) + var/atom/movable/item = new processed_slime.slime_type.core_type(drop_location()) adjust_item_drop_location(item) - SSblackbox.record_feedback("tally", "slime_core_harvested", 1, processed_slime.colour) + SSblackbox.record_feedback("tally", "slime_core_harvested", 1, processed_slime.slime_type.colour) return ..() #undef PROCESSOR_SELECT_RECIPE diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index cf581ac97ee..a39567d1131 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -604,7 +604,7 @@ else var/obj/item/slime_extract/to_integrate = human_owner.get_active_held_item() - if(!istype(to_integrate) || to_integrate.Uses <= 0) + if(!istype(to_integrate) || to_integrate.extract_uses <= 0) human_owner.balloon_alert(human_owner, "need an unused slime extract!") return if(!human_owner.temporarilyRemoveItemFromInventory(to_integrate)) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index b21e728b54b..3138f4451fd 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -285,25 +285,25 @@ return TRUE -/mob/living/attack_slime(mob/living/simple_animal/slime/M, list/modifiers) - if(M.buckled) - if(M in buckled_mobs) - M.Feedstop() +/mob/living/attack_slime(mob/living/simple_animal/slime/attacking_slime, list/modifiers) + if(attacking_slime.buckled) + if(attacking_slime in buckled_mobs) + attacking_slime.stop_feeding() return // can't attack while eating! - if(HAS_TRAIT(src, TRAIT_PACIFISM)) - to_chat(M, span_warning("You don't want to hurt anyone!")) + if(HAS_TRAIT(attacking_slime, TRAIT_PACIFISM)) + to_chat(attacking_slime, span_warning("You don't want to hurt anyone!")) return FALSE - if(check_block(src, M.melee_damage_upper, "[M]'s glomp", MELEE_ATTACK, M.armour_penetration, M.melee_damage_type)) + if(check_block(src, attacking_slime.melee_damage_upper, "[attacking_slime]'s glomp", MELEE_ATTACK, attacking_slime.armour_penetration, attacking_slime.melee_damage_type)) return FALSE if (stat != DEAD) - log_combat(M, src, "attacked") - M.do_attack_animation(src) - visible_message(span_danger("\The [M.name] glomps [src]!"), \ - span_userdanger("\The [M.name] glomps you!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, M) - to_chat(M, span_danger("You glomp [src]!")) + log_combat(attacking_slime, src, "attacked") + attacking_slime.do_attack_animation(src) + visible_message(span_danger("\The [attacking_slime.name] glomps [src]!"), \ + span_userdanger("\The [attacking_slime.name] glomps you!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, attacking_slime) + to_chat(attacking_slime, span_danger("You glomp [src]!")) return TRUE return FALSE diff --git a/code/modules/mob/living/simple_animal/slime/death.dm b/code/modules/mob/living/simple_animal/slime/death.dm index 03cb70cbc47..033d7d2414e 100644 --- a/code/modules/mob/living/simple_animal/slime/death.dm +++ b/code/modules/mob/living/simple_animal/slime/death.dm @@ -3,23 +3,23 @@ return if(!gibbed) if(is_adult) - var/mob/living/simple_animal/slime/M = new(drop_location(), colour) - M.rabid = TRUE - M.regenerate_icons() + var/mob/living/simple_animal/slime/new_slime = new(drop_location(), slime_type.type) + new_slime.rabid = TRUE + new_slime.regenerate_icons() is_adult = FALSE maxHealth = 150 - for(var/datum/action/innate/slime/reproduce/R in actions) - R.Remove(src) - var/datum/action/innate/slime/evolve/E = new - E.Grant(src) + for(var/datum/action/innate/slime/reproduce/reproduce_action in actions) + reproduce_action.Remove(src) + var/datum/action/innate/slime/evolve/evolve_action = new + evolve_action.Grant(src) revive(HEAL_ALL) regenerate_icons() update_name() return if(buckled) - Feedstop(silent = TRUE) //releases ourselves from the mob we fed on. + stop_feeding(silent = TRUE) //releases ourselves from the mob we fed on. cut_overlays() diff --git a/code/modules/mob/living/simple_animal/slime/life.dm b/code/modules/mob/living/simple_animal/slime/life.dm index 3c34a68d6a2..078ec2749b5 100644 --- a/code/modules/mob/living/simple_animal/slime/life.dm +++ b/code/modules/mob/living/simple_animal/slime/life.dm @@ -1,3 +1,7 @@ +#define SLIME_HUNGER_NONE 0 +#define SLIME_HUNGER_HUNGRY 1 +#define SLIME_HUNGER_STARVING 2 + /mob/living/simple_animal/slime/Life(seconds_per_tick = SSMOBS_DT, times_fired) if(HAS_TRAIT(src, TRAIT_NO_TRANSFORM)) return @@ -31,21 +35,21 @@ return return ..() +/// the master AI process +/mob/living/simple_animal/slime/proc/process_slime_ai() -/mob/living/simple_animal/slime/proc/AIprocess() // the master AI process - - if(AIproc || stat || client) + if(slime_ai_processing || stat || client) return - var/hungry = 0 + var/hungry = SLIME_HUNGER_NONE if (nutrition < get_starve_nutrition()) - hungry = 2 + hungry = SLIME_HUNGER_STARVING else if (nutrition < get_grow_nutrition() && prob(25) || nutrition < get_hunger_nutrition()) - hungry = 1 + hungry = SLIME_HUNGER_HUNGRY - AIproc = 1 + slime_ai_processing = TRUE - while(AIproc && stat != DEAD && (attacked || hungry || rabid || buckled)) + while(slime_ai_processing && stat != DEAD && (attacked_stacks || hungry || rabid || buckled)) if(!(mobility_flags & MOBILITY_MOVE)) //also covers buckling. Not sure why buckled is in the while condition if we're going to immediately break, honestly break @@ -54,22 +58,22 @@ if(Target.health <= -70 || Target.stat == DEAD) set_target(null) - AIproc = 0 + slime_ai_processing = FALSE break if(Target) if(locate(/mob/living/simple_animal/slime) in Target.buckled_mobs) set_target(null) - AIproc = 0 + slime_ai_processing = FALSE break - if(!AIproc) + if(!slime_ai_processing) break if(Target in view(1,src)) - if(!CanFeedon(Target)) //If they're not able to be fed upon, ignore them. - if(!Atkcool) - Atkcool = TRUE - addtimer(VARSET_CALLBACK(src, Atkcool, FALSE), 4.5 SECONDS) + if(!can_feed_on(Target)) //If they're not able to be fed upon, ignore them. + if(!is_attack_on_cooldown) + is_attack_on_cooldown = TRUE + addtimer(VARSET_CALLBACK(src, is_attack_on_cooldown, FALSE), 4.5 SECONDS) if(Target.Adjacent(src)) Target.attack_slime(src) @@ -77,20 +81,20 @@ if((Target.body_position == STANDING_UP) && prob(80)) if(Target.client && Target.health >= 20) - if(!Atkcool) - Atkcool = TRUE - addtimer(VARSET_CALLBACK(src, Atkcool, FALSE), 4.5 SECONDS) + if(!is_attack_on_cooldown) + is_attack_on_cooldown = TRUE + addtimer(VARSET_CALLBACK(src, is_attack_on_cooldown, FALSE), 4.5 SECONDS) if(Target.Adjacent(src)) Target.attack_slime(src) else - if(!Atkcool && Target.Adjacent(src)) - Feedon(Target) + if(!is_attack_on_cooldown && Target.Adjacent(src)) + start_feeding(Target) else - if(!Atkcool && Target.Adjacent(src)) - Feedon(Target) + if(!is_attack_on_cooldown && Target.Adjacent(src)) + start_feeding(Target) else if(Target in view(7, src)) if(!Target.Adjacent(src)) @@ -98,7 +102,7 @@ step_to(src, Target) else set_target(null) - AIproc = 0 + slime_ai_processing = FALSE break var/sleeptime = cached_multiplicative_slowdown @@ -107,7 +111,7 @@ sleep(sleeptime + 2) // this is about as fast as a player slime can go - AIproc = 0 + slime_ai_processing = FALSE /mob/living/simple_animal/slime/handle_environment(datum/gas_mixture/environment, seconds_per_tick, times_fired) var/loc_temp = get_temperature(environment) @@ -159,15 +163,16 @@ updatehealth() +///Handles the slime draining the target it is attached to /mob/living/simple_animal/slime/proc/handle_feeding(seconds_per_tick, times_fired) var/mob/living/prey = buckled if(stat) - Feedstop(silent = TRUE) + stop_feeding(silent = TRUE) if(prey.stat == DEAD) // our victim died if(!client) - if(!rabid && !attacked) + if(!rabid && !attacked_stacks) var/mob/last_to_hurt = prey.LAssailant?.resolve() if(last_to_hurt && last_to_hurt != prey) if(SPT_PROB(30, seconds_per_tick)) @@ -177,9 +182,9 @@ if(prey.client && ishuman(prey)) if(SPT_PROB(61, seconds_per_tick)) - rabid = 1 //we go rabid after finishing to feed on a human with a client. + rabid = TRUE //we go rabid after finishing to feed on a human with a client. - Feedstop() + stop_feeding() return if(iscarbon(prey)) @@ -206,11 +211,11 @@ animal_victim.updatehealth() if(totaldamage <= 0) //if we did no(or negative!) damage to it, stop - Feedstop(0, 0) + stop_feeding(FALSE, FALSE) return else - Feedstop(0, 0) + stop_feeding(FALSE, FALSE) return add_nutrition((rand(7, 15) * 0.5 * seconds_per_tick * CONFIG_GET(number/damage_multiplier))) @@ -218,6 +223,7 @@ //Heal yourself. adjustBruteLoss(-1.5 * seconds_per_tick) +///Handles the slime's nutirion level /mob/living/simple_animal/slime/proc/handle_nutrition(seconds_per_tick, times_fired) if(docile) //God as my witness, I will never go hungry again @@ -243,6 +249,7 @@ else Evolve() +///Adds nutrition to the slime's nutrition level. Has a chance to increase its electric levels. /mob/living/simple_animal/slime/proc/add_nutrition(nutrition_to_add = 0) set_nutrition(min((nutrition + nutrition_to_add), get_max_nutrition())) if(nutrition >= get_grow_nutrition()) @@ -254,124 +261,124 @@ if(prob(25-powerlevel*5)) powerlevel++ - - - +///Handles selecting targets /mob/living/simple_animal/slime/proc/handle_targets(seconds_per_tick, times_fired) - if(attacked > 50) - attacked = 50 + if(attacked_stacks > 50) + attacked_stacks = 50 - if(attacked > 0) - attacked-- + if(attacked_stacks > 0) + attacked_stacks-- - if(Discipline > 0) + if(discipline_stacks > 0) - if(Discipline >= 5 && rabid) + if(discipline_stacks >= 5 && rabid) if(SPT_PROB(37, seconds_per_tick)) - rabid = 0 + rabid = FALSE if(SPT_PROB(5, seconds_per_tick)) - Discipline-- + discipline_stacks-- - if(!client) - if(!(mobility_flags & MOBILITY_MOVE)) - return + if(client) //player controlled slimes can decide for themselves + return - if(buckled) - return // if it's eating someone already, continue eating! + if(!(mobility_flags & MOBILITY_MOVE)) + return - if(Target) - --target_patience - if (target_patience <= 0 || SStun > world.time || Discipline || attacked || docile) // Tired of chasing or something draws out attention - target_patience = 0 - set_target(null) + if(buckled) + return // if it's eating someone already, continue eating! - if(AIproc && SStun > world.time) - return + if(Target) + --target_patience + if (target_patience <= 0 || stunned_until > world.time || discipline_stacks || attacked_stacks || docile) // Tired of chasing or something draws out attention + target_patience = 0 + set_target(null) - var/hungry = 0 // determines if the slime is hungry + if(slime_ai_processing && stunned_until > world.time) + return - if (nutrition < get_starve_nutrition()) - hungry = 2 - else if (nutrition < get_grow_nutrition() && SPT_PROB(13, seconds_per_tick) || nutrition < get_hunger_nutrition()) - hungry = 1 + var/hungry = SLIME_HUNGER_NONE // determines if the slime is hungry - if(hungry == 2 && !client) // if a slime is starving, it starts losing its friends - if(Friends.len > 0 && SPT_PROB(0.5, seconds_per_tick)) - var/mob/nofriend = pick(Friends) - add_friendship(nofriend, -1) + if (nutrition < get_starve_nutrition()) + hungry = SLIME_HUNGER_STARVING + else if (nutrition < get_grow_nutrition() && SPT_PROB(13, seconds_per_tick) || nutrition < get_hunger_nutrition()) + hungry = SLIME_HUNGER_HUNGRY - if(!Target) - if(will_hunt() && hungry || attacked || rabid) // Only add to the list if we need to - var/list/targets = list() + if(hungry == SLIME_HUNGER_STARVING && !client) // if a slime is starving, it starts losing its friends + if(Friends.len > 0 && SPT_PROB(0.5, seconds_per_tick)) + var/mob/nofriend = pick(Friends) + add_friendship(nofriend, -1) - for(var/mob/living/L in view(7,src)) + if(!Target) //If we have no target, try to add a target + if(will_hunt() && hungry || attacked_stacks || rabid) // Only add to the list if we need to + var/list/targets = list() - if(isslime(L) || L.stat == DEAD) // Ignore other slimes and dead mobs - continue - - if(L in Friends) // No eating friends! - continue - - var/ally = FALSE - for(var/F in faction) - if(F == FACTION_NEUTRAL) //slimes are neutral so other mobs not target them, but they can target neutral mobs - continue - if(F in L.faction) - ally = TRUE - break - if(ally) - continue - - if(issilicon(L) && (rabid || attacked)) // They can't eat silicons, but they can glomp them in defence - targets += L // Possible target found! - - if(locate(/mob/living/simple_animal/slime) in L.buckled_mobs) // Only one slime can latch on at a time. + for(var/mob/living/L in view(7,src)) + + if(isslime(L) || L.stat == DEAD) // Ignore other slimes and dead mobs + continue + + if(L in Friends) // No eating friends! + continue + + var/ally = FALSE + for(var/F in faction) + if(F == FACTION_NEUTRAL) //slimes are neutral so other mobs not target them, but they can target neutral mobs continue + if(F in L.faction) + ally = TRUE + break + if(ally) + continue + if(issilicon(L) && (rabid || attacked_stacks)) // They can't eat silicons, but they can glomp them in defence targets += L // Possible target found! - if(targets.len > 0) - if(attacked || rabid || hungry == 2) - set_target(targets[1]) // I am attacked and am fighting back or so hungry I don't even care - else - for(var/mob/living/carbon/C in targets) - if(!Discipline && SPT_PROB(2.5, seconds_per_tick)) - if(ishuman(C) || isalienadult(C)) - set_target(C) - break + if(locate(/mob/living/simple_animal/slime) in L.buckled_mobs) // Only one slime can latch on at a time. + continue - if(islarva(C) || ismonkey(C)) + targets += L // Possible target found! + + if(targets.len > 0) + if(attacked_stacks || rabid || hungry == SLIME_HUNGER_STARVING) + set_target(targets[1]) // I am attacked and am fighting back or so hungry I don't even care + else + for(var/mob/living/carbon/C in targets) + if(!discipline_stacks && SPT_PROB(2.5, seconds_per_tick)) + if(ishuman(C) || isalienadult(C)) set_target(C) break - if (Target) - target_patience = rand(5, 7) - if (is_adult) - target_patience += 3 + if(islarva(C) || ismonkey(C)) + set_target(C) + break - if(!Target) // If we have no target, we are wandering or following orders - if (Leader) - if(holding_still) - holding_still = max(holding_still - (0.5 * seconds_per_tick), 0) - else if(!HAS_TRAIT(src, TRAIT_IMMOBILIZED) && isturf(loc)) - step_to(src, Leader) + if (Target) + target_patience = rand(5, 7) + if (is_adult) + target_patience += 3 - else if(hungry) - if (holding_still) - holding_still = max(holding_still - (0.5 * hungry * seconds_per_tick), 0) - else if(!HAS_TRAIT(src, TRAIT_IMMOBILIZED) && isturf(loc) && prob(50)) - step(src, pick(GLOB.cardinals)) + if(!Target) // If we have no target, we are wandering or following orders + if (Leader) + if(holding_still) + holding_still = max(holding_still - (0.5 * seconds_per_tick), 0) + else if(!HAS_TRAIT(src, TRAIT_IMMOBILIZED) && isturf(loc)) + step_to(src, Leader) - else - if(holding_still) - holding_still = max(holding_still - (0.5 * seconds_per_tick), 0) - else if (docile && pulledby) - holding_still = 10 - else if(!HAS_TRAIT(src, TRAIT_IMMOBILIZED) && isturf(loc) && prob(33)) - step(src, pick(GLOB.cardinals)) - else if(!AIproc) - INVOKE_ASYNC(src, PROC_REF(AIprocess)) + else if(hungry) + if (holding_still) + holding_still = max(holding_still - (0.5 * hungry * seconds_per_tick), 0) + else if(!HAS_TRAIT(src, TRAIT_IMMOBILIZED) && isturf(loc) && prob(50)) + step(src, pick(GLOB.cardinals)) + + else + if(holding_still) + holding_still = max(holding_still - (0.5 * seconds_per_tick), 0) + else if (docile && pulledby) + holding_still = 10 + else if(!HAS_TRAIT(src, TRAIT_IMMOBILIZED) && isturf(loc) && prob(33)) + step(src, pick(GLOB.cardinals)) + else if(!slime_ai_processing) + INVOKE_ASYNC(src, PROC_REF(process_slime_ai)) /mob/living/simple_animal/slime/handle_automated_movement() return //slime random movement is currently handled in handle_targets() @@ -379,22 +386,30 @@ /mob/living/simple_animal/slime/handle_automated_speech() return //slime random speech is currently handled in handle_speech() +///Handles slime mood /mob/living/simple_animal/slime/proc/handle_mood(seconds_per_tick, times_fired) - var/newmood = "" - if (rabid || attacked) - newmood = "angry" + #define SLIME_MOOD_NONE "" + #define SLIME_MOOD_ANGRY "angry" + #define SLIME_MOOD_MISCHIEVOUS "mischievous" + #define SLIME_MOOD_POUT "pout" + #define SLIME_MOOD_SAD "sad" + #define SLIME_MOOD_SMILE ":3" + + var/newmood = SLIME_MOOD_NONE + if (rabid || attacked_stacks) + newmood = SLIME_MOOD_ANGRY else if (docile) - newmood = ":3" + newmood = SLIME_MOOD_SMILE else if (Target) - newmood = "mischievous" + newmood = SLIME_MOOD_MISCHIEVOUS if (!newmood) - if (Discipline && SPT_PROB(13, seconds_per_tick)) - newmood = "pout" + if (discipline_stacks && SPT_PROB(13, seconds_per_tick)) + newmood = SLIME_MOOD_POUT else if (SPT_PROB(0.5, seconds_per_tick)) - newmood = pick("sad", ":3", "pout") + newmood = pick(SLIME_MOOD_SAD, ":3", SLIME_MOOD_POUT) - if ((current_mood == "sad" || current_mood == ":3" || current_mood == "pout") && !newmood) + if ((current_mood == SLIME_MOOD_SAD || current_mood == SLIME_MOOD_SMILE || current_mood == SLIME_MOOD_POUT) && !newmood) if(SPT_PROB(50, seconds_per_tick)) newmood = current_mood @@ -402,13 +417,21 @@ current_mood = newmood regenerate_icons() + #undef SLIME_MOOD_NONE + #undef SLIME_MOOD_ANGRY + #undef SLIME_MOOD_MISCHIEVOUS + #undef SLIME_MOOD_POUT + #undef SLIME_MOOD_SAD + #undef SLIME_MOOD_SMILE + +///Handles the slime understanding commends spoken to it /mob/living/simple_animal/slime/proc/handle_speech(seconds_per_tick, times_fired) //Speech understanding starts here var/to_say if (speech_buffer.len > 0) var/who = speech_buffer[1] // Who said it? var/phrase = speech_buffer[2] // What did they say? - if ((findtext(phrase, num2text(number)) || findtext(phrase, "slimes"))) // Talking to us + if ((findtext(phrase, num2text(slime_id)) || findtext(phrase, "slimes"))) // Talking to us if (findtext(phrase, "hello") || findtext(phrase, "hi")) to_say = pick("Hello...", "Hi...") else if (findtext(phrase, "follow")) @@ -429,7 +452,7 @@ else if (findtext(phrase, "stop")) if (buckled) // We are asked to stop feeding if (Friends[who] >= SLIME_FRIENDSHIP_STOPEAT) - Feedstop() + stop_feeding() set_target(null) if (Friends[who] < SLIME_FRIENDSHIP_STOPEAT_NOANGRY) add_friendship(who, -1) @@ -473,20 +496,20 @@ else if (findtext(phrase, "attack")) if (rabid && prob(20)) set_target(who) - AIprocess() //Wake up the slime's Target AI, needed otherwise this doesn't work + process_slime_ai() //Wake up the slime's Target AI, needed otherwise this doesn't work to_say = "ATTACK!?!?" else if (Friends[who] >= SLIME_FRIENDSHIP_ATTACK) - for (var/mob/living/L in view(7,src)-list(src,who)) - if (findtext(phrase, lowertext(L.name))) - if (isslime(L)) - to_say = "NO... [L] slime friend" + for (var/mob/living/possible_target in view(7,src)-list(src,who)) + if (findtext(phrase, lowertext(possible_target.name))) + if (isslime(possible_target)) + to_say = "NO... [possible_target] slime friend" add_friendship(who, -1) //Don't ask a slime to attack its friend - else if(!Friends[L] || Friends[L] < 1) - set_target(L) - AIprocess()//Wake up the slime's Target AI, needed otherwise this doesn't work + else if(!Friends[possible_target] || Friends[possible_target] < 1) + set_target(possible_target) + process_slime_ai()//Wake up the slime's Target AI, needed otherwise this doesn't work to_say = "Ok... I attack [Target]" else - to_say = "No... like [L] ..." + to_say = "No... like [possible_target] ..." add_friendship(who, -1) //Don't ask a slime to attack its friend break else @@ -500,23 +523,23 @@ else if(SPT_PROB(0.5, seconds_per_tick)) emote(pick("bounce","sway","light","vibrate","jiggle")) else - var/t = 10 + var/speech_chance = 10 var/slimes_near = 0 var/dead_slimes = 0 var/friends_near = list() - for (var/mob/living/L in view(7,src)) - if(isslime(L) && L != src) + for (var/mob/living/seen_mob in view(7,src)) + if(isslime(seen_mob) && seen_mob != src) ++slimes_near - if (L.stat == DEAD) + if (seen_mob.stat == DEAD) ++dead_slimes - if (L in Friends) - t += 20 - friends_near += L + if (seen_mob in Friends) + speech_chance += 20 + friends_near += seen_mob if (nutrition < get_hunger_nutrition()) - t += 10 + speech_chance += 10 if (nutrition < get_starve_nutrition()) - t += 10 - if (SPT_PROB(1, seconds_per_tick) && prob(t)) + speech_chance += 10 + if (SPT_PROB(1, seconds_per_tick) && prob(speech_chance)) var/phrases = list() if (Target) phrases += "[Target]... look yummy..." @@ -532,13 +555,13 @@ phrases += "Rawr..." phrases += "Blop..." phrases += "Blorble..." - if (rabid || attacked) + if (rabid || attacked_stacks) phrases += "Hrr..." phrases += "Nhuu..." phrases += "Unn..." if (current_mood == ":3") phrases += "Purr..." - if (attacked) + if (attacked_stacks) phrases += "Grrr..." if (bodytemperature < T0C) phrases += "Cold..." @@ -567,44 +590,52 @@ phrases += "What happened?" if (!slimes_near) phrases += "Lonely..." - for (var/M in friends_near) - phrases += "[M]... friend..." + for (var/friend in friends_near) + phrases += "[friend]... friend..." if (nutrition < get_hunger_nutrition()) - phrases += "[M]... feed me..." + phrases += "[friend]... feed me..." if(!stat) say (pick(phrases)) -/mob/living/simple_animal/slime/proc/get_max_nutrition() // Can't go above it +/// Can't go above it +/mob/living/simple_animal/slime/proc/get_max_nutrition() if (is_adult) return 1200 else return 1000 -/mob/living/simple_animal/slime/proc/get_grow_nutrition() // Above it we grow, below it we can eat +/// Above it we grow, below it we can eat +/mob/living/simple_animal/slime/proc/get_grow_nutrition() if (is_adult) return 1000 else return 800 -/mob/living/simple_animal/slime/proc/get_hunger_nutrition() // Below it we will always eat +/// Below it we will always eat +/mob/living/simple_animal/slime/proc/get_hunger_nutrition() if (is_adult) return 600 else return 500 -/mob/living/simple_animal/slime/proc/get_starve_nutrition() // Below it we will eat before everything else +/// Below it we will eat before everything else +/mob/living/simple_animal/slime/proc/get_starve_nutrition() if(is_adult) return 300 else return 200 - -/mob/living/simple_animal/slime/proc/will_hunt(hunger = -1) // Check for being stopped from feeding and chasing +/// Check for being stopped from feeding and chasing +/mob/living/simple_animal/slime/proc/will_hunt(hunger = -1) if (docile) return FALSE - if (hunger == 2 || rabid || attacked) + if (hunger == SLIME_HUNGER_STARVING || rabid || attacked_stacks) return TRUE if (Leader) return FALSE if (holding_still) return FALSE return TRUE + +#undef SLIME_HUNGER_NONE +#undef SLIME_HUNGER_HUNGRY +#undef SLIME_HUNGER_STARVING diff --git a/code/modules/mob/living/simple_animal/slime/powers.dm b/code/modules/mob/living/simple_animal/slime/powers.dm index a485b8342ca..32c60f63adc 100644 --- a/code/modules/mob/living/simple_animal/slime/powers.dm +++ b/code/modules/mob/living/simple_animal/slime/powers.dm @@ -16,9 +16,9 @@ . = ..() if(!.) return - var/mob/living/simple_animal/slime/S = owner + var/mob/living/simple_animal/slime/slime_owner = owner if(needs_growth == GROWTH_NEEDED) - if(S.amount_grown >= SLIME_EVOLUTION_THRESHOLD) + if(slime_owner.amount_grown >= SLIME_EVOLUTION_THRESHOLD) return TRUE return FALSE return TRUE @@ -39,8 +39,8 @@ if(isnull(choice)) return FALSE var/mob/living/victim = choice - if(CanFeedon(victim)) - Feedon(victim) + if(can_feed_on(victim)) + start_feeding(victim) return TRUE return FALSE @@ -50,15 +50,16 @@ /datum/action/innate/slime/feed/Activate() - var/mob/living/simple_animal/slime/S = owner - S.Feed() + var/mob/living/simple_animal/slime/slime_owner = owner + slime_owner.Feed() -/mob/living/simple_animal/slime/proc/CanFeedon(mob/living/meal, silent = FALSE) +///Can the slime leech life energy from the target? +/mob/living/simple_animal/slime/proc/can_feed_on(mob/living/meal, silent = FALSE) if(!Adjacent(meal)) return FALSE if(buckled) - Feedstop() + stop_feeding() return FALSE if(issilicon(meal) || meal.mob_biotypes & MOB_ROBOTIC) @@ -120,16 +121,18 @@ return FALSE return TRUE -/mob/living/simple_animal/slime/proc/Feedon(mob/living/M) - M.unbuckle_all_mobs(force=1) //Slimes rip other mobs (eg: shoulder parrots) off (Slimes Vs Slimes is already handled in CanFeedon()) - if(M.buckle_mob(src, force=TRUE)) - layer = M.layer+0.01 //appear above the target mob - M.visible_message(span_danger("[name] latches onto [M]!"), \ - span_userdanger("[name] latches onto [M]!")) +///The slime will start feeding on the target +/mob/living/simple_animal/slime/proc/start_feeding(mob/living/target_mob) + target_mob.unbuckle_all_mobs(force=TRUE) //Slimes rip other mobs (eg: shoulder parrots) off (Slimes Vs Slimes is already handled in can_feed_on()) + if(target_mob.buckle_mob(src, force=TRUE)) + layer = target_mob.layer+0.01 //appear above the target mob + target_mob.visible_message(span_danger("[name] latches onto [target_mob]!"), \ + span_userdanger("[name] latches onto [target_mob]!")) else to_chat(src, span_warning("I have failed to latch onto the subject!")) -/mob/living/simple_animal/slime/proc/Feedstop(silent = FALSE, living=1) +///The slime will stop feeding +/mob/living/simple_animal/slime/proc/stop_feeding(silent = FALSE, living=TRUE) if(buckled) if(!living) to_chat(src, "[pick("This subject is incompatible", \ @@ -142,7 +145,7 @@ if(istype(victim)) var/bio_protection = 100 - victim.getarmor(null, BIO) if(prob(bio_protection)) - victim.apply_status_effect(/datum/status_effect/slimed, slime_colours_to_rgb[colour], colour == SLIME_TYPE_RAINBOW) + victim.apply_status_effect(/datum/status_effect/slimed, slime_type.rgb_code, slime_type.colour == SLIME_TYPE_RAINBOW) if(!silent) visible_message(span_warning("[src] lets go of [buckled]!"), \ @@ -157,20 +160,21 @@ if(stat) to_chat(src, "I must be conscious to do this...") return - if(!is_adult) - if(amount_grown >= SLIME_EVOLUTION_THRESHOLD) - is_adult = 1 - maxHealth = 200 - amount_grown = 0 - for(var/datum/action/innate/slime/evolve/E in actions) - E.Remove(src) - GRANT_ACTION(/datum/action/innate/slime/reproduce) - regenerate_icons() - update_name() - else - to_chat(src, "I am not ready to evolve yet...") - else + if(is_adult) to_chat(src, "I have already evolved...") + return + if(amount_grown < SLIME_EVOLUTION_THRESHOLD) + to_chat(src, "I am not ready to evolve yet...") + return + + is_adult = TRUE + maxHealth = 200 + amount_grown = 0 + for(var/datum/action/innate/slime/evolve/evolve_action in actions) + evolve_action.Remove(src) + GRANT_ACTION(/datum/action/innate/slime/reproduce) + regenerate_icons() + update_name() /datum/action/innate/slime/evolve name = "Evolve" @@ -178,8 +182,8 @@ needs_growth = GROWTH_NEEDED /datum/action/innate/slime/evolve/Activate() - var/mob/living/simple_animal/slime/S = owner - S.Evolve() + var/mob/living/simple_animal/slime/slime_owner = owner + slime_owner.Evolve() /mob/living/simple_animal/slime/verb/Reproduce() set category = "Slime" @@ -209,11 +213,11 @@ var/child_colour if(mutation_chance >= 100) - child_colour = SLIME_TYPE_RAINBOW + child_colour = /datum/slime_type/rainbow else if(prob(mutation_chance)) - child_colour = slime_mutation[rand(1,4)] + child_colour = pick_weight(slime_type.mutations) else - child_colour = colour + child_colour = slime_type.type var/mob/living/simple_animal/slime/baby baby = new(drop_loc, child_colour) @@ -228,7 +232,7 @@ baby.set_friends(Friends) babies += baby baby.mutation_chance = clamp(mutation_chance+(rand(5,-5)),0,100) - SSblackbox.record_feedback("tally", "slime_babies_born", 1, baby.colour) + SSblackbox.record_feedback("tally", "slime_babies_born", 1, baby.slime_type.colour) var/mob/living/simple_animal/slime/new_slime = pick(babies) // slime that the OG slime will move into. new_slime.set_combat_mode(TRUE) diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index 3de2bf73ece..a15c1ffd45b 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -40,126 +40,95 @@ footstep_type = FOOTSTEP_MOB_SLIME + //Physiology + + ///Is the slime an adult slime? + var/is_adult = TRUE + + ///The number of /obj/item/slime_extract's the slime has left inside + var/cores = 1 + ///Chance of mutating, should be between 25 and 35 + var/mutation_chance = 30 + ///1-10 controls how much electricity they are generating + var/powerlevel = 0 + ///Controls how long the slime has been overfed, if 10, grows or reproduces + var/amount_grown = 0 + + ///Has a mutator been used on the slime? Only one is allowed + var/mutator_used = FALSE + ///Is the slime forced into being immobile, despite the gases present? + var/force_stasis = FALSE + + //The datum that handles the slime colour's core and possible mutations + var/datum/slime_type/slime_type + + //CORE-CROSSING CODE + + ///What cross core modification is being used. + var/crossbreed_modification + ///How many extracts of the modtype have been applied. + var/applied_crossbreed_amount = 0 + + //AI related traits + ///The current mood of the slime, set randomly or through emotes (if sentient). var/current_mood - var/AIproc = 0 // determines if the AI loop is activated - var/Atkcool = 0 // attack cooldown - var/Discipline = 0 // if a slime has been hit with a freeze gun, or wrestled/attacked off a human, they become disciplined and don't attack anymore for a while - var/SStun = 0 // stun variable + ///Determines if the AI loop is activated + var/slime_ai_processing = FALSE + ///Attack cooldown + var/is_attack_on_cooldown = FALSE + ///If a slime has been hit with a freeze gun, or wrestled/attacked off a human, they become disciplined and don't attack anymore for a while + var/discipline_stacks = 0 + ///Stored the world time when the slime's stun wears off + var/stunned_until = 0 - var/is_adult = 0 - var/docile = 0 + ///Is the slime docile? + var/docile = FALSE - var/cores = 1 // the number of /obj/item/slime_extract's the slime has left inside - var/mutation_chance = 30 // Chance of mutating, should be between 25 and 35 + ///Used to understand when someone is talking to it + var/slime_id = 0 + ///AI variable - tells the slime to hunt this down + var/mob/living/Target = null + ///AI variable - tells the slime to follow this person + var/mob/living/Leader = null - var/powerlevel = 0 // 1-10 controls how much electricity they are generating - var/amount_grown = 0 // controls how long the slime has been overfed, if 10, grows or reproduces + ///Determines if it's been attacked recently. Can be any number, is a cooloff-ish variable + var/attacked_stacks = 0 + ///If set to 1, the slime will attack and eat anything it comes in contact with + var/rabid = FALSE + ///AI variable, cooloff-ish for how long it's going to stay in one place + var/holding_still = 0 + ///AI variable, cooloff-ish for how long it's going to follow its target + var/target_patience = 0 + ///A list of friends; they are not considered targets for feeding; passed down after splitting + var/list/Friends = list() + ///Last phrase said near it and person who said it + var/list/speech_buffer = list() - var/number = 0 // Used to understand when someone is talking to it - - var/mob/living/Target = null // AI variable - tells the slime to hunt this down - var/mob/living/Leader = null // AI variable - tells the slime to follow this person - - var/attacked = 0 // Determines if it's been attacked recently. Can be any number, is a cooloff-ish variable - var/rabid = 0 // If set to 1, the slime will attack and eat anything it comes in contact with - var/holding_still = 0 // AI variable, cooloff-ish for how long it's going to stay in one place - var/target_patience = 0 // AI variable, cooloff-ish for how long it's going to follow its target - - var/list/Friends = list() // A list of friends; they are not considered targets for feeding; passed down after splitting - - var/list/speech_buffer = list() // Last phrase said near it and person who said it - - var/mutator_used = FALSE //So you can't shove a dozen mutators into a single slime - var/force_stasis = FALSE - - var/static/regex/slime_name_regex = new("\\w+ (baby|adult) slime \\(\\d+\\)") - ///////////TIME FOR SUBSPECIES - - var/colour = SLIME_TYPE_GREY - var/coretype = /obj/item/slime_extract/grey - var/list/slime_mutation[4] - - var/static/list/slime_colours = list( - SLIME_TYPE_ADAMANTINE, - SLIME_TYPE_BLACK, - SLIME_TYPE_BLUE, - SLIME_TYPE_BLUESPACE, - SLIME_TYPE_CERULEAN, - SLIME_TYPE_DARK_BLUE, - SLIME_TYPE_DARK_PURPLE, - SLIME_TYPE_GOLD, - SLIME_TYPE_GREEN, - SLIME_TYPE_GREY, - SLIME_TYPE_LIGHT_PINK, - SLIME_TYPE_METAL, - SLIME_TYPE_OIL, - SLIME_TYPE_ORANGE, - SLIME_TYPE_PINK, - SLIME_TYPE_PURPLE, - SLIME_TYPE_PYRITE, - SLIME_TYPE_RAINBOW, - SLIME_TYPE_RED, - SLIME_TYPE_SEPIA, - SLIME_TYPE_SILVER, - SLIME_TYPE_YELLOW, - ) - - var/static/list/slime_colours_to_rgb = list( - SLIME_TYPE_ADAMANTINE = COLOR_SLIME_ADAMANTINE, - SLIME_TYPE_BLACK = COLOR_SLIME_BLACK, - SLIME_TYPE_BLUE = COLOR_SLIME_BLUE, - SLIME_TYPE_BLUESPACE = COLOR_SLIME_BLUESPACE, - SLIME_TYPE_CERULEAN = COLOR_SLIME_CERULEAN, - SLIME_TYPE_DARK_BLUE = COLOR_SLIME_DARK_BLUE, - SLIME_TYPE_DARK_PURPLE = COLOR_SLIME_DARK_PURPLE, - SLIME_TYPE_GOLD = COLOR_SLIME_GOLD, - SLIME_TYPE_GREEN = COLOR_SLIME_GREEN, - SLIME_TYPE_GREY = COLOR_SLIME_GREY, - SLIME_TYPE_LIGHT_PINK = COLOR_SLIME_LIGHT_PINK, - SLIME_TYPE_METAL = COLOR_SLIME_METAL, - SLIME_TYPE_OIL = COLOR_SLIME_OIL, - SLIME_TYPE_ORANGE = COLOR_SLIME_ORANGE, - SLIME_TYPE_PINK = COLOR_SLIME_PINK, - SLIME_TYPE_PURPLE = COLOR_SLIME_PURPLE, - SLIME_TYPE_PYRITE = COLOR_SLIME_PYRITE, - SLIME_TYPE_RAINBOW = COLOR_SLIME_RAINBOW, - SLIME_TYPE_RED = COLOR_SLIME_RED, - SLIME_TYPE_SEPIA = COLOR_SLIME_SEPIA, - SLIME_TYPE_SILVER = COLOR_SLIME_SILVER, - SLIME_TYPE_YELLOW = COLOR_SLIME_YELLOW, - ) - - ///////////CORE-CROSSING CODE - - var/effectmod //What core modification is being used. - var/applied = 0 //How many extracts of the modtype have been applied. - - -/mob/living/simple_animal/slime/Initialize(mapload, new_colour=colour, new_is_adult=FALSE) - var/datum/action/innate/slime/feed/F = new - F.Grant(src) - ADD_TRAIT(src, TRAIT_CANT_RIDE, INNATE_TRAIT) +/mob/living/simple_animal/slime/Initialize(mapload, new_type=/datum/slime_type/grey, new_is_adult=FALSE) + var/datum/action/innate/slime/feed/feeding_action = new + feeding_action.Grant(src) is_adult = new_is_adult if(is_adult) - var/datum/action/innate/slime/reproduce/R = new - R.Grant(src) + var/datum/action/innate/slime/reproduce/reproduce_action = new + reproduce_action.Grant(src) health = 200 maxHealth = 200 else - var/datum/action/innate/slime/evolve/E = new - E.Grant(src) + var/datum/action/innate/slime/evolve/evolve_action = new + evolve_action.Grant(src) create_reagents(100) - set_colour(new_colour) + set_slime_type(new_type) . = ..() set_nutrition(700) - add_cell_sample() - ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT) AddElement(/datum/element/soft_landing) + AddElement(/datum/element/swabable, CELL_LINE_TABLE_SLIME, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) + ADD_TRAIT(src, TRAIT_CANT_RIDE, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT) /mob/living/simple_animal/slime/Destroy() for (var/A in actions) @@ -170,6 +139,14 @@ clear_friends() return ..() +///Random slime subtype +/mob/living/simple_animal/slime/random/Initialize(mapload, new_colour, new_is_adult) + . = ..(mapload, pick(subtypesof(/datum/slime_type)), prob(50)) + +///Friendly docile subtype +/mob/living/simple_animal/slime/pet + docile = TRUE + /mob/living/simple_animal/slime/create_reagents(max_vol, flags) . = ..() RegisterSignals(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_DEL_REAGENT), PROC_REF(on_reagent_change)) @@ -181,27 +158,27 @@ UnregisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_QDELETING)) return NONE -/mob/living/simple_animal/slime/proc/set_colour(new_colour) - colour = new_colour +/mob/living/simple_animal/slime/proc/set_slime_type(new_type) + slime_type = new new_type update_name() - slime_mutation = mutation_table(colour) - var/sanitizedcolour = replacetext(colour, " ", "") - coretype = text2path("/obj/item/slime_extract/[sanitizedcolour]") regenerate_icons() /mob/living/simple_animal/slime/update_name() + ///Checks if the slime has a generic name, in the format of baby/adult slime (123) + var/static/regex/slime_name_regex = new("\\w+ (baby|adult) slime \\(\\d+\\)") if(slime_name_regex.Find(name)) - number = rand(1, 1000) - name = "[colour] [is_adult ? "adult" : "baby"] slime ([number])" + slime_id = rand(1, 1000) + name = "[slime_type.colour] [is_adult ? "adult" : "baby"] slime ([slime_id])" real_name = name return ..() +///randomizes the colour of a slime /mob/living/simple_animal/slime/proc/random_colour() - set_colour(pick(slime_colours)) + set_slime_type(pick(subtypesof(/datum/slime_type))) /mob/living/simple_animal/slime/regenerate_icons() cut_overlays() - var/icon_text = "[colour] [is_adult ? "adult" : "baby"] slime" + var/icon_text = "[slime_type.colour] [is_adult ? "adult" : "baby"] slime" icon_dead = "[icon_text] dead" if(stat != DEAD) icon_state = icon_text @@ -267,11 +244,11 @@ probab = 95 if(prob(probab)) if(istype(O, /obj/structure/window) || istype(O, /obj/structure/grille)) - if(nutrition <= get_hunger_nutrition() && !Atkcool) + if(nutrition <= get_hunger_nutrition() && !is_attack_on_cooldown) if (is_adult || prob(5)) O.attack_slime(src) - Atkcool = TRUE - addtimer(VARSET_CALLBACK(src, Atkcool, FALSE), 4.5 SECONDS) + is_attack_on_cooldown = TRUE + addtimer(VARSET_CALLBACK(src, is_attack_on_cooldown, FALSE), 4.5 SECONDS) /mob/living/simple_animal/slime/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return 2 @@ -304,51 +281,51 @@ return powerlevel = 0 // oh no, the power! -/mob/living/simple_animal/slime/MouseDrop(atom/movable/A as mob|obj) - if(isliving(A) && A != src && usr == src) - var/mob/living/Food = A - if(CanFeedon(Food)) - Feedon(Food) +/mob/living/simple_animal/slime/MouseDrop(atom/movable/target_atom as mob|obj) + if(isliving(target_atom) && target_atom != src && usr == src) + var/mob/living/Food = target_atom + if(can_feed_on(Food)) + start_feeding(Food) return ..() -/mob/living/simple_animal/slime/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE, silent = FALSE) +/mob/living/simple_animal/slime/doUnEquip(obj/item/unequipped_item, force, newloc, no_move, invdrop = TRUE, silent = FALSE) return -/mob/living/simple_animal/slime/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) +/mob/living/simple_animal/slime/start_pulling(atom/movable/moveable_atom, state, force = move_force, supress_message = FALSE) return /mob/living/simple_animal/slime/attack_ui(slot, params) return -/mob/living/simple_animal/slime/attack_slime(mob/living/simple_animal/slime/M, list/modifiers) +/mob/living/simple_animal/slime/attack_slime(mob/living/simple_animal/slime/attacking_slime, list/modifiers) if(..()) //successful slime attack - if(M == src) + if(attacking_slime == src) return if(buckled) - Feedstop(silent = TRUE) - visible_message(span_danger("[M] pulls [src] off!"), \ + stop_feeding(silent = TRUE) + visible_message(span_danger("[attacking_slime] pulls [src] off!"), \ span_danger("You pull [src] off!")) return - attacked += 5 + attacked_stacks += 5 if(nutrition >= 100) //steal some nutrition. negval handled in life() - adjust_nutrition(-(50 + (40 * M.is_adult))) - M.add_nutrition(50 + (40 * M.is_adult)) + adjust_nutrition(-(50 + (40 * attacking_slime.is_adult))) + attacking_slime.add_nutrition(50 + (40 * attacking_slime.is_adult)) if(health > 0) - M.adjustBruteLoss(-10 + (-10 * M.is_adult)) - M.updatehealth() + attacking_slime.adjustBruteLoss(-10 + (-10 * attacking_slime.is_adult)) + attacking_slime.updatehealth() /mob/living/simple_animal/slime/attack_animal(mob/living/simple_animal/user, list/modifiers) . = ..() if(.) - attacked += 10 + attacked_stacks += 10 /mob/living/simple_animal/slime/attack_paw(mob/living/carbon/human/user, list/modifiers) if(..()) //successful monkey bite. - attacked += 10 + attacked_stacks += 10 /mob/living/simple_animal/slime/attack_larva(mob/living/carbon/alien/larva/L, list/modifiers) if(..()) //successful larva bite. - attacked += 10 + attacked_stacks += 10 /mob/living/simple_animal/slime/attack_hulk(mob/living/carbon/human/user) . = ..() @@ -391,92 +368,101 @@ if(operations.next_step(user, modifiers)) return TRUE if(..()) //successful attack - attacked += 10 + attacked_stacks += 10 /mob/living/simple_animal/slime/attack_alien(mob/living/carbon/alien/adult/user, list/modifiers) if(..()) //if harm or disarm intent. - attacked += 10 + attacked_stacks += 10 discipline_slime(user) -/mob/living/simple_animal/slime/attackby(obj/item/W, mob/living/user, params) +/mob/living/simple_animal/slime/attackby(obj/item/attacking_item, mob/living/user, params) if(stat == DEAD && surgeries.len) var/list/modifiers = params2list(params) if(!user.combat_mode || (LAZYACCESS(modifiers, RIGHT_CLICK))) for(var/datum/surgery/operations as anything in surgeries) if(operations.next_step(user, modifiers)) return TRUE - if(istype(W, /obj/item/stack/sheet/mineral/plasma) && !stat) //Let's you feed slimes plasma. + if(istype(attacking_item, /obj/item/stack/sheet/mineral/plasma) && !stat) //Let's you feed slimes plasma. add_friendship(user, 1) to_chat(user, span_notice("You feed the slime the plasma. It chirps happily.")) - var/obj/item/stack/sheet/mineral/plasma/S = W - S.use(1) + var/obj/item/stack/sheet/mineral/plasma/sheet = attacking_item + sheet.use(1) return - if(W.force > 0) - attacked += 10 + if(attacking_item.force > 0) + attacked_stacks += 10 if(prob(25)) user.do_attack_animation(src) user.changeNext_move(CLICK_CD_MELEE) - to_chat(user, span_danger("[W] passes right through [src]!")) + to_chat(user, span_danger("[attacking_item] passes right through [src]!")) return - if(Discipline && prob(50)) // wow, buddy, why am I getting attacked?? - Discipline = 0 - if(W.force >= 3) - var/force_effect = 2 * W.force + if(discipline_stacks && prob(50)) // wow, buddy, why am I getting attacked?? + discipline_stacks = 0 + if(attacking_item.force >= 3) + var/force_effect = 2 * attacking_item.force if(is_adult) - force_effect = round(W.force/2) + force_effect = round(attacking_item.force/2) if(prob(10 + force_effect)) discipline_slime(user) - if(istype(W, /obj/item/storage/bag/xeno)) - var/obj/item/storage/P = W - if(!effectmod) - to_chat(user, span_warning("The slime is not currently being mutated.")) - return - var/hasOutput = FALSE //Have we outputted text? - var/hasFound = FALSE //Have we found an extract to be added? - for(var/obj/item/slime_extract/S in P.contents) - if(S.effectmod == effectmod) - P.atom_storage.attempt_remove(S, get_turf(src), silent = TRUE) - qdel(S) - applied++ - hasFound = TRUE - if(applied >= SLIME_EXTRACT_CROSSING_REQUIRED) - to_chat(user, span_notice("You feed the slime as many of the extracts from the bag as you can, and it mutates!")) - playsound(src, 'sound/effects/attackblob.ogg', 50, TRUE) - spawn_corecross() - hasOutput = TRUE - break - if(!hasOutput) - if(!hasFound) - to_chat(user, span_warning("There are no extracts in the bag that this slime will accept!")) - else - to_chat(user, span_notice("You feed the slime some extracts from the bag.")) - playsound(src, 'sound/effects/attackblob.ogg', 50, TRUE) - return - ..() + if(!istype(attacking_item, /obj/item/storage/bag/xeno)) + return ..() + + var/obj/item/storage/xeno_bag = attacking_item + if(!crossbreed_modification) + to_chat(user, span_warning("The slime is not currently being mutated.")) + return + var/has_output = FALSE //Have we outputted text? + var/has_found = FALSE //Have we found an extract to be added? + for(var/obj/item/slime_extract/extract in xeno_bag.contents) + if(extract.crossbreed_modification == crossbreed_modification) + xeno_bag.atom_storage.attempt_remove(extract, get_turf(src), silent = TRUE) + qdel(extract) + applied_crossbreed_amount++ + has_found = TRUE + if(applied_crossbreed_amount >= SLIME_EXTRACT_CROSSING_REQUIRED) + to_chat(user, span_notice("You feed the slime as many of the extracts from the bag as you can, and it mutates!")) + playsound(src, 'sound/effects/attackblob.ogg', 50, TRUE) + spawn_corecross() + has_output = TRUE + break + + if(has_output) + return + + if(!has_found) + to_chat(user, span_warning("There are no extracts in the bag that this slime will accept!")) + else + to_chat(user, span_notice("You feed the slime some extracts from the bag.")) + playsound(src, 'sound/effects/attackblob.ogg', 50, TRUE) + return + + +///Spawns a crossed slimecore item /mob/living/simple_animal/slime/proc/spawn_corecross() var/static/list/crossbreeds = subtypesof(/obj/item/slimecross) visible_message(span_danger("[src] shudders, its mutated core consuming the rest of its body!")) playsound(src, 'sound/magic/smoke.ogg', 50, TRUE) - var/crosspath - for(var/X in crossbreeds) - var/obj/item/slimecross/S = X - if(initial(S.colour) == colour && initial(S.effect) == effectmod) - crosspath = S + var/selected_crossbreed_path + for(var/crossbreed_path in crossbreeds) + var/obj/item/slimecross/cross_item = crossbreed_path + if(initial(cross_item.colour) == slime_type.colour && initial(cross_item.effect) == crossbreed_modification) + selected_crossbreed_path = cross_item break - if(crosspath) - new crosspath(loc) + if(selected_crossbreed_path) + new selected_crossbreed_path(loc) else visible_message(span_warning("The mutated core shudders, and collapses into a puddle, unable to maintain its form.")) qdel(src) /mob/living/simple_animal/slime/proc/apply_water() adjustBruteLoss(rand(15,20)) - if(!client) - if(Target) // Like cats - set_target(null) - ++Discipline + if(client) + return + + if(Target) // Like cats + set_target(null) + ++discipline_stacks return /mob/living/simple_animal/slime/examine(mob/user) @@ -509,22 +495,22 @@ . += "" +///Makes a slime not attack people for a while /mob/living/simple_animal/slime/proc/discipline_slime(mob/user) if(stat) return if(prob(80) && !client) - Discipline++ + discipline_stacks++ - if(!is_adult) - if(Discipline == 1) - attacked = 0 + if(!is_adult && discipline_stacks == 1) //if the slime is a baby and has not been overly disciplined, it will give up its grudge + attacked_stacks = 0 set_target(null) if(buckled) - Feedstop(silent = TRUE) //we unbuckle the slime from the mob it latched onto. + stop_feeding(silent = TRUE) //we unbuckle the slime from the mob it latched onto. - SStun = world.time + rand(20,60) + stunned_until = world.time + rand(2 SECONDS, 6 SECONDS) Stun(3) if(user) @@ -532,25 +518,16 @@ addtimer(CALLBACK(src, PROC_REF(slime_move), user), 0.3 SECONDS) - +///Makes a slime move away, used for a timed callback /mob/living/simple_animal/slime/proc/slime_move(mob/user) if(user) step_away(src,user,15) - -/mob/living/simple_animal/slime/pet - docile = 1 - /mob/living/simple_animal/slime/get_mob_buckling_height(mob/seat) if(..()) return 3 -/mob/living/simple_animal/slime/random/Initialize(mapload, new_colour, new_is_adult) - . = ..(mapload, pick(slime_colours), prob(50)) - -/mob/living/simple_animal/slime/add_cell_sample() - AddElement(/datum/element/swabable, CELL_LINE_TABLE_SLIME, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) - +///Sets the slime's current attack target /mob/living/simple_animal/slime/proc/set_target(new_target) var/old_target = Target Target = new_target @@ -559,6 +536,7 @@ if(Target) RegisterSignal(Target, COMSIG_QDELETING, PROC_REF(clear_memories_of), override = TRUE) +///Sets the person the slime is following around /mob/living/simple_animal/slime/proc/set_leader(new_leader) var/old_leader = Leader Leader = new_leader @@ -567,6 +545,7 @@ if(Leader) RegisterSignal(Leader, COMSIG_QDELETING, PROC_REF(clear_memories_of), override = TRUE) +///Alters the friendship value of the target /mob/living/simple_animal/slime/proc/add_friendship(new_friend, amount = 1) if(!Friends[new_friend]) Friends[new_friend] = 0 @@ -574,25 +553,30 @@ if(new_friend) RegisterSignal(new_friend, COMSIG_QDELETING, PROC_REF(clear_memories_of), override = TRUE) +///Sets the friendship value of the target /mob/living/simple_animal/slime/proc/set_friendship(new_friend, amount = 1) Friends[new_friend] = amount if(new_friend) RegisterSignal(new_friend, COMSIG_QDELETING, PROC_REF(clear_memories_of), override = TRUE) +///Removes someone from the friendlist /mob/living/simple_animal/slime/proc/remove_friend(friend) Friends -= friend if(friend && !SLIME_CARES_ABOUT(friend)) UnregisterSignal(friend, COMSIG_QDELETING) +///Adds someone to the friend list /mob/living/simple_animal/slime/proc/set_friends(new_buds) clear_friends() for(var/mob/friend as anything in new_buds) set_friendship(friend, new_buds[friend]) +///Removes everyone from the friend list /mob/living/simple_animal/slime/proc/clear_friends() for(var/mob/friend as anything in Friends) remove_friend(friend) +///The passed source will be no longer be the slime's target, leader, or one of its friends /mob/living/simple_animal/slime/proc/clear_memories_of(datum/source) SIGNAL_HANDLER if(source == Target) diff --git a/code/modules/mob/living/simple_animal/slime/slime_type.dm b/code/modules/mob/living/simple_animal/slime/slime_type.dm new file mode 100644 index 00000000000..11f37988040 --- /dev/null +++ b/code/modules/mob/living/simple_animal/slime/slime_type.dm @@ -0,0 +1,224 @@ +/datum/slime_type + ///Our slime's colour as text. Used by both description, and icon + var/colour + ///The type our slime spawns + var/core_type + ///The possible mutations of our slime + var/list/mutations + ///The hexcode used by the slime to colour their victims + var/rgb_code + +//TIER 0 + +/datum/slime_type/grey + colour = SLIME_TYPE_GREY + core_type = /obj/item/slime_extract/grey + mutations = list( + /datum/slime_type/blue = 1, + /datum/slime_type/metal = 1, + /datum/slime_type/orange = 1, + /datum/slime_type/purple = 1, + ) + rgb_code = COLOR_SLIME_GREY + + +//TIER 1 + +/datum/slime_type/blue + colour = SLIME_TYPE_BLUE + core_type = /obj/item/slime_extract/blue + mutations = list( + /datum/slime_type/darkblue = 1, + /datum/slime_type/pink = 2, + /datum/slime_type/silver = 1, + ) + rgb_code = COLOR_SLIME_BLUE + +/datum/slime_type/metal + colour = SLIME_TYPE_METAL + core_type = /obj/item/slime_extract/metal + mutations = list( + /datum/slime_type/gold = 2, + /datum/slime_type/silver = 1, + /datum/slime_type/yellow = 1, + ) + rgb_code = COLOR_SLIME_METAL + +/datum/slime_type/purple + colour = SLIME_TYPE_PURPLE + core_type = /obj/item/slime_extract/purple + mutations = list( + /datum/slime_type/darkblue = 1, + /datum/slime_type/darkpurple = 1, + /datum/slime_type/green = 2, + ) + rgb_code = COLOR_SLIME_PURPLE + +/datum/slime_type/orange + colour = SLIME_TYPE_ORANGE + core_type = /obj/item/slime_extract/orange + mutations = list( + /datum/slime_type/darkpurple = 1, + /datum/slime_type/red = 2, + /datum/slime_type/yellow = 1, + ) + rgb_code = COLOR_SLIME_ORANGE + +//TIER 2 + +/datum/slime_type/darkblue + colour = SLIME_TYPE_DARK_BLUE + core_type = /obj/item/slime_extract/darkblue + mutations = list( + /datum/slime_type/blue = 1, + /datum/slime_type/cerulean = 2, + /datum/slime_type/purple = 1, + ) + rgb_code = COLOR_SLIME_BLUE + +/datum/slime_type/darkpurple + colour = SLIME_TYPE_DARK_PURPLE + core_type = /obj/item/slime_extract/darkpurple + mutations = list( + /datum/slime_type/orange = 1, + /datum/slime_type/purple = 1, + /datum/slime_type/sepia = 2, + ) + rgb_code = COLOR_SLIME_PURPLE + +/datum/slime_type/silver + colour = SLIME_TYPE_SILVER + core_type = /obj/item/slime_extract/silver + mutations = list( + /datum/slime_type/blue = 1, + /datum/slime_type/metal = 1, + /datum/slime_type/pyrite = 2, + ) + rgb_code = COLOR_SLIME_SILVER + +/datum/slime_type/yellow + colour = SLIME_TYPE_YELLOW + core_type = /obj/item/slime_extract/yellow + mutations = list( + /datum/slime_type/bluespace = 2, + /datum/slime_type/metal = 1, + /datum/slime_type/orange = 1, + ) + rgb_code = COLOR_SLIME_YELLOW + +//TIER 3 + +/datum/slime_type/bluespace + colour = SLIME_TYPE_BLUESPACE + core_type = /obj/item/slime_extract/bluespace + mutations = list( + /datum/slime_type/bluespace = 1, + ) + rgb_code = COLOR_SLIME_BLUESPACE + + +/datum/slime_type/cerulean + colour = SLIME_TYPE_CERULEAN + core_type = /obj/item/slime_extract/cerulean + mutations = list( + /datum/slime_type/cerulean = 1, + ) + rgb_code = COLOR_SLIME_CERULEAN + +/datum/slime_type/pyrite + colour = SLIME_TYPE_PYRITE + core_type = /obj/item/slime_extract/pyrite + mutations = list( + /datum/slime_type/pyrite = 1, + ) + rgb_code = COLOR_SLIME_PYRITE + +/datum/slime_type/sepia + colour = SLIME_TYPE_SEPIA + core_type = /obj/item/slime_extract/sepia + mutations = list( + /datum/slime_type/sepia = 1, + ) + rgb_code = COLOR_SLIME_SEPIA + +//TIER 4 + +/datum/slime_type/gold + colour = SLIME_TYPE_GOLD + core_type = /obj/item/slime_extract/gold + mutations = list( + /datum/slime_type/adamantine = 1, + /datum/slime_type/gold = 1, + ) + rgb_code = COLOR_SLIME_GOLD + +/datum/slime_type/green + colour = SLIME_TYPE_GREEN + core_type = /obj/item/slime_extract/green + mutations = list( + /datum/slime_type/black = 1, + /datum/slime_type/green = 1, + ) + rgb_code = COLOR_SLIME_GREEN + +/datum/slime_type/pink + colour = SLIME_TYPE_PINK + core_type = /obj/item/slime_extract/pink + mutations = list( + /datum/slime_type/lightpink = 1, + /datum/slime_type/pink = 1, + ) + rgb_code = COLOR_SLIME_PINK + +/datum/slime_type/red + colour = SLIME_TYPE_RED + core_type = /obj/item/slime_extract/red + mutations = list( + /datum/slime_type/oil = 1, + /datum/slime_type/red = 1, + ) + rgb_code = COLOR_SLIME_RED + +//TIER 5 + +/datum/slime_type/adamantine + colour = SLIME_TYPE_ADAMANTINE + core_type = /obj/item/slime_extract/adamantine + mutations = list( + /datum/slime_type/adamantine = 1, + ) + rgb_code = COLOR_SLIME_ADAMANTINE + +/datum/slime_type/black + colour = SLIME_TYPE_BLACK + core_type = /obj/item/slime_extract/black + mutations = list( + /datum/slime_type/black = 1, + ) + rgb_code = COLOR_SLIME_BLACK + +/datum/slime_type/lightpink + colour = SLIME_TYPE_LIGHT_PINK + core_type = /obj/item/slime_extract/lightpink + mutations = list( + /datum/slime_type/lightpink = 1, + ) + rgb_code = COLOR_SLIME_LIGHT_PINK + +/datum/slime_type/oil + colour = SLIME_TYPE_OIL + core_type = /obj/item/slime_extract/oil + mutations = list( + /datum/slime_type/oil = 1, + ) + rgb_code = COLOR_SLIME_OIL + +//Tier Special + +/datum/slime_type/rainbow + colour = SLIME_TYPE_RAINBOW + core_type = /obj/item/slime_extract/rainbow + mutations = list( + /datum/slime_type/rainbow = 1, + ) + rgb_code = COLOR_SLIME_RAINBOW diff --git a/code/modules/mob/living/simple_animal/slime/subtypes.dm b/code/modules/mob/living/simple_animal/slime/subtypes.dm deleted file mode 100644 index e948de7cd4d..00000000000 --- a/code/modules/mob/living/simple_animal/slime/subtypes.dm +++ /dev/null @@ -1,79 +0,0 @@ -/mob/living/simple_animal/slime/proc/mutation_table(colour) - var/list/slime_mutation_colors[4] - switch(colour) - //Tier 1 - if(SLIME_TYPE_GREY) - slime_mutation_colors[1] = SLIME_TYPE_ORANGE - slime_mutation_colors[2] = SLIME_TYPE_METAL - slime_mutation_colors[3] = SLIME_TYPE_BLUE - slime_mutation_colors[4] = SLIME_TYPE_PURPLE - //Tier 2 - if(SLIME_TYPE_PURPLE) - slime_mutation_colors[1] = SLIME_TYPE_DARK_PURPLE - slime_mutation_colors[2] = SLIME_TYPE_DARK_BLUE - slime_mutation_colors[3] = SLIME_TYPE_GREEN - slime_mutation_colors[4] = SLIME_TYPE_GREEN - if(SLIME_TYPE_METAL) - slime_mutation_colors[1] = SLIME_TYPE_SILVER - slime_mutation_colors[2] = SLIME_TYPE_YELLOW - slime_mutation_colors[3] = SLIME_TYPE_GOLD - slime_mutation_colors[4] = SLIME_TYPE_GOLD - if(SLIME_TYPE_ORANGE) - slime_mutation_colors[1] = SLIME_TYPE_DARK_PURPLE - slime_mutation_colors[2] = SLIME_TYPE_YELLOW - slime_mutation_colors[3] = SLIME_TYPE_RED - slime_mutation_colors[4] = SLIME_TYPE_RED - if(SLIME_TYPE_BLUE) - slime_mutation_colors[1] = SLIME_TYPE_DARK_BLUE - slime_mutation_colors[2] = SLIME_TYPE_SILVER - slime_mutation_colors[3] = SLIME_TYPE_PINK - slime_mutation_colors[4] = SLIME_TYPE_PINK - //Tier 3 - if(SLIME_TYPE_DARK_BLUE) - slime_mutation_colors[1] = SLIME_TYPE_PURPLE - slime_mutation_colors[2] = SLIME_TYPE_BLUE - slime_mutation_colors[3] = SLIME_TYPE_CERULEAN - slime_mutation_colors[4] = SLIME_TYPE_CERULEAN - if(SLIME_TYPE_DARK_PURPLE) - slime_mutation_colors[1] = SLIME_TYPE_PURPLE - slime_mutation_colors[2] = SLIME_TYPE_ORANGE - slime_mutation_colors[3] = SLIME_TYPE_SEPIA - slime_mutation_colors[4] = SLIME_TYPE_SEPIA - if(SLIME_TYPE_YELLOW) - slime_mutation_colors[1] = SLIME_TYPE_METAL - slime_mutation_colors[2] = SLIME_TYPE_ORANGE - slime_mutation_colors[3] = SLIME_TYPE_BLUESPACE - slime_mutation_colors[4] = SLIME_TYPE_BLUESPACE - if(SLIME_TYPE_SILVER) - slime_mutation_colors[1] = SLIME_TYPE_METAL - slime_mutation_colors[2] = SLIME_TYPE_BLUE - slime_mutation_colors[3] = SLIME_TYPE_PYRITE - slime_mutation_colors[4] = SLIME_TYPE_PYRITE - //Tier 4 - if(SLIME_TYPE_PINK) - slime_mutation_colors[1] = SLIME_TYPE_PINK - slime_mutation_colors[2] = SLIME_TYPE_PINK - slime_mutation_colors[3] = SLIME_TYPE_LIGHT_PINK - slime_mutation_colors[4] = SLIME_TYPE_LIGHT_PINK - if(SLIME_TYPE_RED) - slime_mutation_colors[1] = SLIME_TYPE_RED - slime_mutation_colors[2] = SLIME_TYPE_RED - slime_mutation_colors[3] = SLIME_TYPE_OIL - slime_mutation_colors[4] = SLIME_TYPE_OIL - if(SLIME_TYPE_GOLD) - slime_mutation_colors[1] = SLIME_TYPE_GOLD - slime_mutation_colors[2] = SLIME_TYPE_GOLD - slime_mutation_colors[3] = SLIME_TYPE_ADAMANTINE - slime_mutation_colors[4] = SLIME_TYPE_ADAMANTINE - if(SLIME_TYPE_GREEN) - slime_mutation_colors[1] = SLIME_TYPE_GREEN - slime_mutation_colors[2] = SLIME_TYPE_GREEN - slime_mutation_colors[3] = SLIME_TYPE_BLACK - slime_mutation_colors[4] = SLIME_TYPE_BLACK - // Tier 5 - else - slime_mutation_colors[1] = colour - slime_mutation_colors[2] = colour - slime_mutation_colors[3] = colour - slime_mutation_colors[4] = colour - return(slime_mutation_colors) diff --git a/code/modules/mob_spawn/corpses/nonhuman_corpses.dm b/code/modules/mob_spawn/corpses/nonhuman_corpses.dm index ce02c6894ae..3a63a246035 100644 --- a/code/modules/mob_spawn/corpses/nonhuman_corpses.dm +++ b/code/modules/mob_spawn/corpses/nonhuman_corpses.dm @@ -22,11 +22,11 @@ icon = 'icons/mob/simple/slimes.dmi' icon_state = "grey baby slime" //sets the icon in the map editor ///the color of the slime you're spawning. - var/slime_species = "grey" + var/slime_species = /datum/slime_type/grey /obj/effect/mob_spawn/corpse/slime/special(mob/living/simple_animal/slime/spawned_slime) . = ..() - spawned_slime.set_colour(slime_species) + spawned_slime.set_slime_type(slime_species) ///dead facehuggers, great for xeno ruins so you can have a cool ruin without spiraling the entire round into xenomorph hell. also, this is a terrible terrible artifact of time /obj/effect/mob_spawn/corpse/facehugger diff --git a/code/modules/reagents/chemistry/holder/reactions.dm b/code/modules/reagents/chemistry/holder/reactions.dm index eb1cfa445a8..5a97c474f70 100644 --- a/code/modules/reagents/chemistry/holder/reactions.dm +++ b/code/modules/reagents/chemistry/holder/reactions.dm @@ -335,8 +335,8 @@ if(istype(cached_my_atom, /obj/item/slime_extract)) var/obj/item/slime_extract/extract = my_atom - extract.Uses-- - if(extract.Uses <= 0) // give the notification that the slime core is dead + extract.extract_uses-- + if(extract.extract_uses <= 0) // give the notification that the slime core is dead my_atom.visible_message(span_notice("[iconhtml] \The [my_atom]'s power is consumed in the reaction.")) extract.name = "used slime extract" extract.desc = "This extract has been used up." diff --git a/code/modules/reagents/chemistry/recipes/slime_extracts.dm b/code/modules/reagents/chemistry/recipes/slime_extracts.dm index 6b5ddfc14e3..f37f9ebb081 100644 --- a/code/modules/reagents/chemistry/recipes/slime_extracts.dm +++ b/code/modules/reagents/chemistry/recipes/slime_extracts.dm @@ -10,7 +10,7 @@ if(!istype(extract)) return FALSE - return extract.Uses > 0 + return extract.extract_uses > 0 /datum/chemical_reaction/slime/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) use_slime_core(holder) @@ -22,7 +22,7 @@ /datum/chemical_reaction/slime/proc/delete_extract(datum/reagents/holder) var/obj/item/slime_extract/M = holder.my_atom - if(M.Uses <= 0 && !results.len) //if the slime doesn't output chemicals + if(M.extract_uses <= 0 && !results.len) //if the slime doesn't output chemicals qdel(M) //Grey @@ -31,8 +31,8 @@ required_container = /obj/item/slime_extract/grey /datum/chemical_reaction/slime/slimespawn/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) - var/mob/living/simple_animal/slime/S = new(get_turf(holder.my_atom), "grey") - S.visible_message(span_danger("Infused with plasma, the core begins to quiver and grow, and a new baby slime emerges from it!")) + var/mob/living/simple_animal/slime/spawning_slime = new(get_turf(holder.my_atom), /datum/slime_type/grey) + spawning_slime.visible_message(span_danger("Infused with plasma, the core begins to quiver and grow, and a new baby slime emerges from it!")) ..() /datum/chemical_reaction/slime/slimeinaprov @@ -319,7 +319,7 @@ slime.docile = FALSE slime.update_name() continue - slime.rabid = 1 + slime.rabid = TRUE slime.visible_message(span_danger("The [slime] is driven into a frenzy!")) ..() @@ -472,7 +472,7 @@ var/turf/T = get_turf(holder.my_atom) new /obj/effect/timestop(T, null, null, null) if(istype(extract)) - if(extract.Uses > 0) + if(extract.extract_uses > 0) var/mob/lastheld = get_mob_by_key(holder.my_atom.fingerprintslast) if(lastheld && !lastheld.equip_to_slot_if_possible(extract, ITEM_SLOT_HANDS, disable_warning = TRUE)) extract.forceMove(get_turf(lastheld)) @@ -527,8 +527,8 @@ S.active = TRUE addtimer(CALLBACK(S, TYPE_PROC_REF(/obj/item/grenade, detonate)), rand(15,60)) else - var/mob/living/simple_animal/slime/random/S = new (get_turf(holder.my_atom)) - S.visible_message(span_danger("Infused with plasma, the core begins to quiver and grow, and a new baby slime emerges from it!")) + var/mob/living/simple_animal/slime/random/random_slime = new (get_turf(holder.my_atom)) + random_slime.visible_message(span_danger("Infused with plasma, the core begins to quiver and grow, and a new baby slime emerges from it!")) ..() /datum/chemical_reaction/slime/slimebomb diff --git a/code/modules/research/xenobiology/crossbreeding/_potions.dm b/code/modules/research/xenobiology/crossbreeding/_potions.dm index 0b5368f5372..7fb7f10849d 100644 --- a/code/modules/research/xenobiology/crossbreeding/_potions.dm +++ b/code/modules/research/xenobiology/crossbreeding/_potions.dm @@ -28,7 +28,7 @@ Slimecrossing Potions return var/path = S.type var/obj/item/slime_extract/C = new path(get_turf(target)) - C.Uses = S.Uses + C.extract_uses = S.extract_uses to_chat(user, span_notice("You pour the potion onto [target], and the fluid solidifies into a copy of it!")) qdel(src) return diff --git a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm index 04d37c62b7c..5669aab44f2 100644 --- a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm +++ b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm @@ -473,10 +473,10 @@ colour = SLIME_TYPE_GREY /datum/status_effect/stabilized/grey/tick(seconds_between_ticks) - for(var/mob/living/simple_animal/slime/S in range(1, get_turf(owner))) - if(!(owner in S.Friends)) - to_chat(owner, span_notice("[linked_extract] pulses gently as it communicates with [S].")) - S.set_friendship(owner, 1) + for(var/mob/living/simple_animal/slime/slimes_in_range in range(1, get_turf(owner))) + if(!(owner in slimes_in_range.Friends)) + to_chat(owner, span_notice("[linked_extract] pulses gently as it communicates with [slimes_in_range].")) + slimes_in_range.set_friendship(owner, 1) return ..() /datum/status_effect/stabilized/orange diff --git a/code/modules/research/xenobiology/crossbreeding/burning.dm b/code/modules/research/xenobiology/crossbreeding/burning.dm index 7c27399b03e..8d849c36569 100644 --- a/code/modules/research/xenobiology/crossbreeding/burning.dm +++ b/code/modules/research/xenobiology/crossbreeding/burning.dm @@ -32,11 +32,11 @@ Burning extracts: effect_desc = "Creates a hungry and speedy slime that will love you forever." /obj/item/slimecross/burning/grey/do_effect(mob/user) - var/mob/living/simple_animal/slime/S = new(get_turf(user),SLIME_TYPE_GREY) - S.visible_message(span_danger("A baby slime emerges from [src], and it nuzzles [user] before burbling hungrily!")) - S.set_friendship(user, 20) //Gas, gas, gas - S.bodytemperature = T0C + 400 //We gonna step on the gas. - S.set_nutrition(S.get_hunger_nutrition()) //Tonight, we fight! + var/mob/living/simple_animal/slime/new_slime = new(get_turf(user),/datum/slime_type/grey) + new_slime.visible_message(span_danger("A baby slime emerges from [src], and it nuzzles [user] before burbling hungrily!")) + new_slime.set_friendship(user, 20) //Gas, gas, gas + new_slime.bodytemperature = T0C + 400 //We gonna step on the gas. + new_slime.set_nutrition(new_slime.get_hunger_nutrition()) //Tonight, we fight! ..() /obj/item/slimecross/burning/orange @@ -197,15 +197,15 @@ Burning extracts: /obj/item/slimecross/burning/red/do_effect(mob/user) user.visible_message(span_danger("[src] pulses a hazy red aura for a moment, which wraps around [user]!")) - for(var/mob/living/simple_animal/slime/S in view(7, get_turf(user))) - if(user in S.Friends) - var/friendliness = S.Friends[user] - S.clear_friends() - S.set_friendship(user, friendliness) + for(var/mob/living/simple_animal/slime/slimes_in_view in view(7, get_turf(user))) + if(user in slimes_in_view.Friends) + var/friendliness = slimes_in_view.Friends[user] + slimes_in_view.clear_friends() + slimes_in_view.set_friendship(user, friendliness) else - S.clear_friends() - S.rabid = 1 - S.visible_message(span_danger("The [S] is driven into a dangerous frenzy!")) + slimes_in_view.clear_friends() + slimes_in_view.rabid = TRUE + slimes_in_view.visible_message(span_danger("The [slimes_in_view] is driven into a dangerous frenzy!")) ..() /obj/item/slimecross/burning/green diff --git a/code/modules/research/xenobiology/crossbreeding/charged.dm b/code/modules/research/xenobiology/crossbreeding/charged.dm index 586a33d2dff..cde5e234afd 100644 --- a/code/modules/research/xenobiology/crossbreeding/charged.dm +++ b/code/modules/research/xenobiology/crossbreeding/charged.dm @@ -278,6 +278,6 @@ Charged extracts: /obj/item/slimecross/charged/rainbow/do_effect(mob/user) user.visible_message(span_warning("[src] swells and splits into three new slimes!")) for(var/i in 1 to 3) - var/mob/living/simple_animal/slime/S = new(get_turf(user)) - S.random_colour() + var/mob/living/simple_animal/slime/new_slime = new(get_turf(user)) + new_slime.random_colour() return ..() diff --git a/code/modules/research/xenobiology/crossbreeding/chilling.dm b/code/modules/research/xenobiology/crossbreeding/chilling.dm index 65c7199a7f0..84b184bf892 100644 --- a/code/modules/research/xenobiology/crossbreeding/chilling.dm +++ b/code/modules/research/xenobiology/crossbreeding/chilling.dm @@ -231,9 +231,9 @@ Chilling extracts: /obj/item/slimecross/chilling/red/do_effect(mob/user) var/slimesfound = FALSE - for(var/mob/living/simple_animal/slime/S in view(get_turf(user), 7)) + for(var/mob/living/simple_animal/slime/slimes_in_view in view(get_turf(user), 7)) slimesfound = TRUE - S.docile = TRUE + slimes_in_view.docile = TRUE if(slimesfound) user.visible_message(span_notice("[src] lets out a peaceful ring as it shatters, and nearby slimes seem calm.")) else diff --git a/code/modules/research/xenobiology/crossbreeding/recurring.dm b/code/modules/research/xenobiology/crossbreeding/recurring.dm index c6ca420cc04..3279e26c920 100644 --- a/code/modules/research/xenobiology/crossbreeding/recurring.dm +++ b/code/modules/research/xenobiology/crossbreeding/recurring.dm @@ -29,10 +29,10 @@ Recurring extracts: /obj/item/slimecross/recurring/process(seconds_per_tick) if(cooldown > 0) cooldown -= seconds_per_tick - else if(extract.Uses < 10 && extract.Uses > 0) - extract.Uses++ + else if(extract.extract_uses < 10 && extract.extract_uses > 0) + extract.extract_uses++ cooldown = max_cooldown - else if(extract.Uses <= 0) + else if(extract.extract_uses <= 0) extract.visible_message(span_warning("The light inside [extract] flickers and dies out.")) extract.desc = "A tiny, inert core, bleeding dark, cerulean-colored goo." extract.icon_state = "prismatic" diff --git a/code/modules/research/xenobiology/crossbreeding/regenerative.dm b/code/modules/research/xenobiology/crossbreeding/regenerative.dm index f0f395b33a6..4a222e8e982 100644 --- a/code/modules/research/xenobiology/crossbreeding/regenerative.dm +++ b/code/modules/research/xenobiology/crossbreeding/regenerative.dm @@ -197,8 +197,8 @@ Regenerative extracts: /obj/item/slimecross/regenerative/green/core_effect(mob/living/target, mob/user) if(isslime(target)) target.visible_message(span_warning("The [target] suddenly changes color!")) - var/mob/living/simple_animal/slime/S = target - S.random_colour() + var/mob/living/simple_animal/slime/target_slime = target + target_slime.random_colour() if(isjellyperson(target)) target.reagents.add_reagent(/datum/reagent/mutationtoxin/jelly,5) diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm index ac148161b03..97e78121908 100644 --- a/code/modules/research/xenobiology/xenobio_camera.dm +++ b/code/modules/research/xenobiology/xenobio_camera.dm @@ -6,16 +6,15 @@ var/allowed_area = null /mob/camera/ai_eye/remote/xenobio/Initialize(mapload) - var/area/A = get_area(loc) - allowed_area = A.name + var/area/our_area = get_area(loc) + allowed_area = our_area.name . = ..() /mob/camera/ai_eye/remote/xenobio/setLoc(turf/destination, force_update = FALSE) var/area/new_area = get_area(destination) + if(new_area && new_area.name == allowed_area || new_area && (new_area.area_flags & XENOBIOLOGY_COMPATIBLE)) return ..() - else - return /mob/camera/ai_eye/remote/xenobio/can_z_move(direction, turf/start, turf/destination, z_move_flags = NONE, mob/living/rider) . = ..() @@ -31,10 +30,15 @@ networks = list("ss13") circuit = /obj/item/circuitboard/computer/xenobiology + ///The recycler connected to the camera console var/obj/machinery/monkey_recycler/connected_recycler + ///The slimes stored inside the console var/list/stored_slimes + ///The single slime potion stored inside the console var/obj/item/slimepotion/slime/current_potion + ///The maximum amount of slimes that fit in the machine var/max_slimes = 5 + ///The amount of monkey cubes inside the machine var/monkeys = 0 icon_screen = "slime_comp" @@ -53,6 +57,9 @@ actions += new /datum/action/innate/hotkey_help(src) stored_slimes = list() + +/obj/machinery/computer/camera_advanced/xenobio/LateInitialize(mapload) + . = ..() for(var/obj/machinery/monkey_recycler/recycler in GLOB.monkey_recyclers) if(get_area(recycler.loc) == get_area(loc)) connected_recycler = recycler @@ -61,8 +68,8 @@ /obj/machinery/computer/camera_advanced/xenobio/Destroy() QDEL_NULL(current_potion) for(var/thing in stored_slimes) - var/mob/living/simple_animal/slime/S = thing - S.forceMove(drop_location()) + var/mob/living/simple_animal/slime/stored_slime = thing + stored_slime.forceMove(drop_location()) stored_slimes.Cut() if(connected_recycler) connected_recycler.connected -= src @@ -92,13 +99,6 @@ RegisterSignal(user, COMSIG_XENO_SLIME_CLICK_SHIFT, PROC_REF(XenoSlimeClickShift)) RegisterSignal(user, COMSIG_XENO_TURF_CLICK_SHIFT, PROC_REF(XenoTurfClickShift)) - //Checks for recycler on every interact, prevents issues with load order on certain maps. - if(!connected_recycler) - for(var/obj/machinery/monkey_recycler/recycler in GLOB.monkey_recyclers) - if(get_area(recycler.loc) == get_area(loc)) - connected_recycler = recycler - connected_recycler.connected += src - /obj/machinery/computer/camera_advanced/xenobio/remove_eye_control(mob/living/user) UnregisterSignal(user, COMSIG_XENO_SLIME_CLICK_CTRL) UnregisterSignal(user, COMSIG_XENO_TURF_CLICK_CTRL) @@ -108,43 +108,116 @@ UnregisterSignal(user, COMSIG_XENO_TURF_CLICK_SHIFT) ..() -/obj/machinery/computer/camera_advanced/xenobio/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/food/monkeycube)) +/obj/machinery/computer/camera_advanced/xenobio/attackby(obj/item/used_item, mob/user, params) + if(istype(used_item, /obj/item/food/monkeycube)) monkeys++ - to_chat(user, span_notice("You feed [O] to [src]. It now has [monkeys] monkey cubes stored.")) - qdel(O) + to_chat(user, span_notice("You feed [used_item] to [src]. It now has [monkeys] monkey cubes stored.")) + qdel(used_item) return - else if(istype(O, /obj/item/storage/bag)) - var/obj/item/storage/P = O + + if(istype(used_item, /obj/item/storage/bag)) + var/obj/item/storage/storage_bag = used_item var/loaded = FALSE - for(var/obj/G in P.contents) - if(istype(G, /obj/item/food/monkeycube)) + for(var/obj/item_in_bag in storage_bag.contents) + if(istype(item_in_bag, /obj/item/food/monkeycube)) loaded = TRUE monkeys++ - qdel(G) + qdel(item_in_bag) if(loaded) - to_chat(user, span_notice("You fill [src] with the monkey cubes stored in [O]. [src] now has [monkeys] monkey cubes stored.")) + to_chat(user, span_notice("You fill [src] with the monkey cubes stored in [used_item]. [src] now has [monkeys] monkey cubes stored.")) return - else if(istype(O, /obj/item/slimepotion/slime)) + + if(istype(used_item, /obj/item/slimepotion/slime)) var/replaced = FALSE - if(user && !user.transferItemToLoc(O, src)) + if(user && !user.transferItemToLoc(used_item, src)) return if(!QDELETED(current_potion)) current_potion.forceMove(drop_location()) replaced = TRUE - current_potion = O - to_chat(user, span_notice("You load [O] in the console's potion slot[replaced ? ", replacing the one that was there before" : ""].")) + current_potion = used_item + to_chat(user, span_notice("You load [used_item] in the console's potion slot[replaced ? ", replacing the one that was there before" : ""].")) return + ..() -/obj/machinery/computer/camera_advanced/xenobio/multitool_act(mob/living/user, obj/item/multitool/I) +/obj/machinery/computer/camera_advanced/xenobio/multitool_act(mob/living/user, obj/item/multitool/used_multitool) . = ..() - if (istype(I) && istype(I.buffer,/obj/machinery/monkey_recycler)) - to_chat(user, span_notice("You link [src] with [I.buffer] in [I] buffer.")) - connected_recycler = I.buffer + if (istype(used_multitool) && istype(used_multitool.buffer,/obj/machinery/monkey_recycler)) + to_chat(user, span_notice("You link [src] with [used_multitool.buffer] in [used_multitool] buffer.")) + connected_recycler = used_multitool.buffer connected_recycler.connected += src return TRUE +/* +Boilerplate check for a valid area to perform a camera action in. +Checks if the AI eye is on a valid turf and then checks if the target turf is xenobiology compatible +Due to keyboard shortcuts, the second one is not necessarily the remote eye's location. +*/ +/obj/machinery/computer/camera_advanced/xenobio/proc/validate_area(mob/living/user, mob/camera/ai_eye/remote/xenobio/remote_eye, turf/open/target_turf) + if(!GLOB.cameranet.checkTurfVis(remote_eye.loc)) + to_chat(user, span_warning("Target is not near a camera. Cannot proceed.")) + return FALSE + + var/area/turfarea = get_area(target_turf) + if(turfarea.name != remote_eye.allowed_area && !(turfarea.area_flags & XENOBIOLOGY_COMPATIBLE)) + to_chat(user, span_warning("Invalid area. Cannot proceed.")) + return FALSE + + return TRUE + +///Places every slime in storage on target turf +/obj/machinery/computer/camera_advanced/xenobio/proc/slime_place(turf/open/target_turf) + for(var/mob/living/simple_animal/slime/stored_slime in stored_slimes) + stored_slime.forceMove(target_turf) + stored_slime.visible_message(span_notice("[stored_slime] warps in!")) + stored_slimes -= stored_slime + +///Places every slime not controlled by a player into the internal storage, respecting its limits +///Returns TRUE to signal it hitting the limit, in case its being called from a loop and we want it to stop +/obj/machinery/computer/camera_advanced/xenobio/proc/slime_pickup(mob/living/user, mob/living/simple_animal/slime/target_slime) + if(stored_slimes.len >= max_slimes) + to_chat(user, span_warning("Slime storage is full.")) + return TRUE + if(target_slime.ckey) + to_chat(user, span_warning("The slime wiggled free!")) + return FALSE + if(target_slime.buckled) + target_slime.stop_feeding(silent = TRUE) + target_slime.visible_message(span_notice("[target_slime] vanishes in a flash of light!")) + target_slime.forceMove(src) + stored_slimes += target_slime + + return FALSE + +///Places one monkey, if possible +/obj/machinery/computer/camera_advanced/xenobio/proc/feed_slime(mob/living/user, turf/open/target_turf) + if(monkeys < 1) + to_chat(user, span_warning("[src] needs to have at least 1 monkey stored. Currently has [monkeys] monkeys stored.")) + return + + var/mob/living/carbon/human/species/monkey/food = new /mob/living/carbon/human/species/monkey(target_turf, TRUE, user) + if (QDELETED(food)) + return + + food.LAssailant = WEAKREF(user) + monkeys-- + monkeys = round(monkeys, 0.1) //Prevents rounding errors + to_chat(user, span_notice("[src] now has [monkeys] monkeys stored.")) + +///Recycles the target monkey +/obj/machinery/computer/camera_advanced/xenobio/proc/monkey_recycle(mob/living/user, mob/living/target_mob) + if(!ismonkey(target_mob)) + return + if(!target_mob.stat) + return + + target_mob.visible_message(span_notice("[target_mob] vanishes as [p_theyre()] reclaimed for recycling!")) + connected_recycler.use_power(500) + monkeys += connected_recycler.cube_production + monkeys = round(monkeys, 0.1) //Prevents rounding errors + qdel(target_mob) + to_chat(user, span_notice("[src] now has [monkeys] monkeys stored.")) + /datum/action/innate/slime_place name = "Place Slimes" button_icon = 'icons/mob/actions/actions_silicon.dmi' @@ -153,17 +226,14 @@ /datum/action/innate/slime_place/Activate() if(!target || !isliving(owner)) return - var/mob/living/C = owner - var/mob/camera/ai_eye/remote/xenobio/remote_eye = C.remote_control - var/obj/machinery/computer/camera_advanced/xenobio/X = target + var/mob/living/owner_mob = owner + var/mob/camera/ai_eye/remote/xenobio/remote_eye = owner_mob.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target - if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) - for(var/mob/living/simple_animal/slime/S in X.stored_slimes) - S.forceMove(remote_eye.loc) - S.visible_message(span_notice("[S] warps in!")) - X.stored_slimes -= S - else - to_chat(owner, span_warning("Target is not near a camera. Cannot proceed.")) + if(!xeno_console.validate_area(owner, remote_eye, remote_eye.loc)) + return + + xeno_console.slime_place(remote_eye.loc) /datum/action/innate/slime_pick_up name = "Pick up Slime" @@ -173,23 +243,16 @@ /datum/action/innate/slime_pick_up/Activate() if(!target || !isliving(owner)) return - var/mob/living/C = owner - var/mob/camera/ai_eye/remote/xenobio/remote_eye = C.remote_control - var/obj/machinery/computer/camera_advanced/xenobio/X = target + var/mob/living/owner_mob = owner + var/mob/camera/ai_eye/remote/xenobio/remote_eye = owner_mob.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target - if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) - for(var/mob/living/simple_animal/slime/S in remote_eye.loc) - if(X.stored_slimes.len >= X.max_slimes) - break - if(!S.ckey) - if(S.buckled) - S.Feedstop(silent = TRUE) - S.visible_message(span_notice("[S] vanishes in a flash of light!")) - S.forceMove(X) - X.stored_slimes += S - else - to_chat(owner, span_warning("Target is not near a camera. Cannot proceed.")) + if(!xeno_console.validate_area(owner, remote_eye, remote_eye.loc)) + return + for(var/mob/living/simple_animal/slime/target_slime in remote_eye.loc) + if(xeno_console.slime_pickup(owner_mob, target_slime)) ///Returns true if we hit our slime pickup limit + break /datum/action/innate/feed_slime name = "Feed Slimes" @@ -199,22 +262,14 @@ /datum/action/innate/feed_slime/Activate() if(!target || !isliving(owner)) return - var/mob/living/C = owner - var/mob/camera/ai_eye/remote/xenobio/remote_eye = C.remote_control - var/obj/machinery/computer/camera_advanced/xenobio/X = target + var/mob/living/living_owner = owner + var/mob/camera/ai_eye/remote/xenobio/remote_eye = living_owner.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target - if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) - if(X.monkeys >= 1) - var/mob/living/carbon/human/species/monkey/food = new /mob/living/carbon/human/species/monkey(remote_eye.loc, TRUE, owner) - if (!QDELETED(food)) - food.LAssailant = WEAKREF(C) - X.monkeys-- - X.monkeys = round(X.monkeys, 0.1) //Prevents rounding errors - to_chat(owner, span_notice("[X] now has [X.monkeys] monkeys stored.")) - else - to_chat(owner, span_warning("[X] needs to have at least 1 monkey stored. Currently has [X.monkeys] monkeys stored.")) - else - to_chat(owner, span_warning("Target is not near a camera. Cannot proceed.")) + if(!xeno_console.validate_area(owner, remote_eye, remote_eye.loc)) + return + + xeno_console.feed_slime(living_owner, remote_eye.loc) /datum/action/innate/monkey_recycle @@ -225,27 +280,20 @@ /datum/action/innate/monkey_recycle/Activate() if(!target || !isliving(owner)) return - var/mob/living/C = owner - var/mob/camera/ai_eye/remote/xenobio/remote_eye = C.remote_control - var/obj/machinery/computer/camera_advanced/xenobio/X = target - var/obj/machinery/monkey_recycler/recycler = X.connected_recycler + var/mob/living/owner_mob = owner + var/mob/camera/ai_eye/remote/xenobio/remote_eye = owner_mob.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target + var/obj/machinery/monkey_recycler/recycler = xeno_console.connected_recycler + + if(!xeno_console.validate_area(owner, remote_eye, remote_eye.loc)) + return if(!recycler) to_chat(owner, span_warning("There is no connected monkey recycler. Use a multitool to link one.")) return - if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) - for(var/mob/living/carbon/human/M in remote_eye.loc) - if(!ismonkey(M)) - continue - if(M.stat) - M.visible_message(span_notice("[M] vanishes as [M.p_theyre()] reclaimed for recycling!")) - recycler.use_power(500) - X.monkeys += recycler.cube_production - X.monkeys = round(X.monkeys, 0.1) //Prevents rounding errors - qdel(M) - to_chat(owner, span_notice("[X] now has [X.monkeys] monkeys available.")) - else - to_chat(owner, span_warning("Target is not near a camera. Cannot proceed.")) + + for(var/mob/living/carbon/human/target_mob in remote_eye.loc) + xeno_console.monkey_recycle(owner, target_mob) /datum/action/innate/slime_scan name = "Scan Slime" @@ -255,14 +303,15 @@ /datum/action/innate/slime_scan/Activate() if(!target || !isliving(owner)) return - var/mob/living/C = owner - var/mob/camera/ai_eye/remote/xenobio/remote_eye = C.remote_control + var/mob/living/owner_mob = owner + var/mob/camera/ai_eye/remote/xenobio/remote_eye = owner_mob.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target - if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) - for(var/mob/living/simple_animal/slime/S in remote_eye.loc) - slime_scan(S, C) - else - to_chat(owner, span_warning("Target is not near a camera. Cannot proceed.")) + if(!xeno_console.validate_area(owner, remote_eye, remote_eye.loc)) + return + + for(var/mob/living/simple_animal/slime/scanned_slime in remote_eye.loc) + slime_scan(scanned_slime, owner_mob) /datum/action/innate/feed_potion name = "Apply Potion" @@ -273,20 +322,20 @@ if(!target || !isliving(owner)) return - var/mob/living/C = owner - var/mob/camera/ai_eye/remote/xenobio/remote_eye = C.remote_control - var/obj/machinery/computer/camera_advanced/xenobio/X = target + var/mob/living/owner_mob = owner + var/mob/camera/ai_eye/remote/xenobio/remote_eye = owner_mob.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = target - if(QDELETED(X.current_potion)) + if(!xeno_console.validate_area(owner, remote_eye, remote_eye.loc)) + return + + if(QDELETED(xeno_console.current_potion)) to_chat(owner, span_warning("No potion loaded.")) return - if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) - for(var/mob/living/simple_animal/slime/S in remote_eye.loc) - X.current_potion.attack(S, C) - break - else - to_chat(owner, span_warning("Target is not near a camera. Cannot proceed.")) + for(var/mob/living/simple_animal/slime/potioned_slime in remote_eye.loc) + xeno_console.current_potion.attack(potioned_slime, owner_mob) + break /datum/action/innate/hotkey_help name = "Hotkey Help" @@ -296,156 +345,123 @@ /datum/action/innate/hotkey_help/Activate() if(!target || !isliving(owner)) return - to_chat(owner, "Click shortcuts:") - to_chat(owner, "Shift-click a slime to pick it up, or the floor to drop all held slimes.") - to_chat(owner, "Ctrl-click a slime to scan it.") - to_chat(owner, "Alt-click a slime to feed it a potion.") - to_chat(owner, "Ctrl-click or a dead monkey to recycle it, or the floor to place a new monkey.") + + var/render_list = list() + render_list += "Click shortcuts:" + render_list += "• Shift-click a slime to pick it up, or the floor to drop all held slimes." + render_list += "• Ctrl-click a slime to scan it." + render_list += "• Alt-click a slime to feed it a potion." + render_list += "• Ctrl-click or a dead monkey to recycle it, or the floor to place a new monkey." + + to_chat(owner, examine_block(jointext(render_list, "\n"))) // // Alternate clicks for slime, monkey and open turf if using a xenobio console -//Feeds a potion to slime /mob/living/simple_animal/slime/AltClick(mob/user) SEND_SIGNAL(user, COMSIG_XENO_SLIME_CLICK_ALT, src) ..() -//Picks up slime /mob/living/simple_animal/slime/ShiftClick(mob/user) SEND_SIGNAL(user, COMSIG_XENO_SLIME_CLICK_SHIFT, src) ..() -//Place slimes /turf/open/ShiftClick(mob/user) SEND_SIGNAL(user, COMSIG_XENO_TURF_CLICK_SHIFT, src) ..() -//scans slimes /mob/living/simple_animal/slime/CtrlClick(mob/user) SEND_SIGNAL(user, COMSIG_XENO_SLIME_CLICK_CTRL, src) ..() -//picks up dead monkies /mob/living/carbon/human/species/monkey/CtrlClick(mob/user) SEND_SIGNAL(user, COMSIG_XENO_MONKEY_CLICK_CTRL, src) ..() -//places monkies /turf/open/CtrlClick(mob/user) SEND_SIGNAL(user, COMSIG_XENO_TURF_CLICK_CTRL, src) ..() -// Scans slime -/obj/machinery/computer/camera_advanced/xenobio/proc/XenoSlimeClickCtrl(mob/living/user, mob/living/simple_animal/slime/S) - SIGNAL_HANDLER - if(!GLOB.cameranet.checkTurfVis(S.loc)) - to_chat(user, span_warning("Target is not near a camera. Cannot proceed.")) - return - var/mob/living/C = user - var/mob/camera/ai_eye/remote/xenobio/E = C.remote_control - var/area/mobarea = get_area(S.loc) - if(mobarea.name == E.allowed_area || (mobarea.area_flags & XENOBIOLOGY_COMPATIBLE)) - slime_scan(S, C) - -//Feeds a potion to slime -/obj/machinery/computer/camera_advanced/xenobio/proc/XenoSlimeClickAlt(mob/living/user, mob/living/simple_animal/slime/S) - SIGNAL_HANDLER - if(!GLOB.cameranet.checkTurfVis(S.loc)) - to_chat(user, span_warning("Target is not near a camera. Cannot proceed.")) - return - var/mob/living/C = user - var/mob/camera/ai_eye/remote/xenobio/E = C.remote_control - var/obj/machinery/computer/camera_advanced/xenobio/X = E.origin - var/area/mobarea = get_area(S.loc) - if(QDELETED(X.current_potion)) - to_chat(C, span_warning("No potion loaded.")) - return - if(mobarea.name == E.allowed_area || (mobarea.area_flags & XENOBIOLOGY_COMPATIBLE)) - INVOKE_ASYNC(X.current_potion, TYPE_PROC_REF(/obj/item/slimepotion/slime, attack), S, C) - -//Picks up slime -/obj/machinery/computer/camera_advanced/xenobio/proc/XenoSlimeClickShift(mob/living/user, mob/living/simple_animal/slime/S) - SIGNAL_HANDLER - if(!GLOB.cameranet.checkTurfVis(S.loc)) - to_chat(user, span_warning("Target is not near a camera. Cannot proceed.")) - return - var/mob/living/C = user - var/mob/camera/ai_eye/remote/xenobio/E = C.remote_control - var/obj/machinery/computer/camera_advanced/xenobio/X = E.origin - var/area/mobarea = get_area(S.loc) - if(mobarea.name == E.allowed_area || (mobarea.area_flags & XENOBIOLOGY_COMPATIBLE)) - if(X.stored_slimes.len >= X.max_slimes) - to_chat(C, span_warning("Slime storage is full.")) - return - if(S.ckey) - to_chat(C, span_warning("The slime wiggled free!")) - return - if(S.buckled) - S.Feedstop(silent = TRUE) - S.visible_message(span_notice("[S] vanishes in a flash of light!")) - S.forceMove(X) - X.stored_slimes += S - -//Place slimes -/obj/machinery/computer/camera_advanced/xenobio/proc/XenoTurfClickShift(mob/living/user, turf/open/T) +/// Scans the target slime +/obj/machinery/computer/camera_advanced/xenobio/proc/XenoSlimeClickCtrl(mob/living/user, mob/living/simple_animal/slime/target_slime) SIGNAL_HANDLER - if(!GLOB.cameranet.checkTurfVis(T)) - to_chat(user, span_warning("Target is not near a camera. Cannot proceed.")) - return - var/mob/living/C = user - var/mob/camera/ai_eye/remote/xenobio/E = C.remote_control - var/obj/machinery/computer/camera_advanced/xenobio/X = E.origin - var/area/turfarea = get_area(T) - if(turfarea.name == E.allowed_area || (turfarea.area_flags & XENOBIOLOGY_COMPATIBLE)) - for(var/mob/living/simple_animal/slime/S in X.stored_slimes) - S.forceMove(T) - S.visible_message(span_notice("[S] warps in!")) - X.stored_slimes -= S + var/mob/camera/ai_eye/remote/xenobio/remote_eye = user.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin -//Place monkey -/obj/machinery/computer/camera_advanced/xenobio/proc/XenoTurfClickCtrl(mob/living/user, turf/open/T) - SIGNAL_HANDLER - if(!GLOB.cameranet.checkTurfVis(T)) - to_chat(user, span_warning("Target is not near a camera. Cannot proceed.")) + if(!xeno_console.validate_area(user, remote_eye, target_slime.loc)) return - var/mob/living/C = user - var/mob/camera/ai_eye/remote/xenobio/E = C.remote_control - var/obj/machinery/computer/camera_advanced/xenobio/X = E.origin - var/area/turfarea = get_area(T) - if(turfarea.name == E.allowed_area || (turfarea.area_flags & XENOBIOLOGY_COMPATIBLE)) - if(X.monkeys >= 1) - var/mob/living/carbon/human/food = new /mob/living/carbon/human/species/monkey(T, TRUE, C) - if (!QDELETED(food)) - food.LAssailant = WEAKREF(C) - X.monkeys-- - X.monkeys = round(X.monkeys, 0.1) //Prevents rounding errors - to_chat(C, span_notice("[X] now has [X.monkeys] monkeys stored.")) - else - to_chat(C, span_warning("[X] needs to have at least 1 monkey stored. Currently has [X.monkeys] monkeys stored.")) -//Pick up monkey -/obj/machinery/computer/camera_advanced/xenobio/proc/XenoMonkeyClickCtrl(mob/living/user, mob/living/carbon/human/M) + slime_scan(target_slime, user) + +///Feeds a stored potion to a slime +/obj/machinery/computer/camera_advanced/xenobio/proc/XenoSlimeClickAlt(mob/living/user, mob/living/simple_animal/slime/target_slime) SIGNAL_HANDLER - if(!ismonkey(M)) + + var/mob/camera/ai_eye/remote/xenobio/remote_eye = user.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin + + if(!xeno_console.validate_area(user, remote_eye, target_slime.loc)) return - if(!isturf(M.loc) || !GLOB.cameranet.checkTurfVis(M.loc)) - to_chat(user, span_warning("Target is not near a camera. Cannot proceed.")) + + if(QDELETED(xeno_console.current_potion)) + to_chat(user, span_warning("No potion loaded.")) return - var/mob/living/C = user - var/mob/camera/ai_eye/remote/xenobio/E = C.remote_control - var/obj/machinery/computer/camera_advanced/xenobio/X = E.origin - var/area/mobarea = get_area(M.loc) - if(!X.connected_recycler) - to_chat(C, span_warning("There is no connected monkey recycler. Use a multitool to link one.")) + + INVOKE_ASYNC(xeno_console.current_potion, TYPE_PROC_REF(/obj/item/slimepotion/slime, attack), target_slime, user) + +///Picks up a slime, and places them in the internal storage +/obj/machinery/computer/camera_advanced/xenobio/proc/XenoSlimeClickShift(mob/living/user, mob/living/simple_animal/slime/target_slime) + SIGNAL_HANDLER + + var/mob/camera/ai_eye/remote/xenobio/remote_eye = user.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin + + if(!xeno_console.validate_area(user, remote_eye, target_slime.loc)) return - if(mobarea.name == E.allowed_area || (mobarea.area_flags & XENOBIOLOGY_COMPATIBLE)) - if(!M.stat) - return - M.visible_message(span_notice("[M] vanishes as [p_theyre()] reclaimed for recycling!")) - X.connected_recycler.use_power(500) - X.monkeys += connected_recycler.cube_production - X.monkeys = round(X.monkeys, 0.1) //Prevents rounding errors - qdel(M) - to_chat(C, span_notice("[X] now has [X.monkeys] monkeys available.")) + + xeno_console.slime_pickup(user, target_slime) + +///Places all slimes from the internal storage +/obj/machinery/computer/camera_advanced/xenobio/proc/XenoTurfClickShift(mob/living/user, turf/open/target_turf) + SIGNAL_HANDLER + + var/mob/living/user_mob = user + var/mob/camera/ai_eye/remote/xenobio/remote_eye = user_mob.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin + + if(!xeno_console.validate_area(user, remote_eye, target_turf)) + return + + slime_place(target_turf) + +///Places a monkey from the internal storage +/obj/machinery/computer/camera_advanced/xenobio/proc/XenoTurfClickCtrl(mob/living/user, turf/open/target_turf) + SIGNAL_HANDLER + + var/mob/camera/ai_eye/remote/xenobio/remote_eye = user.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin + + if(!xeno_console.validate_area(user, remote_eye, target_turf)) + return + + xeno_console.feed_slime(user, target_turf) + +///Picks up a dead monkey for recycling +/obj/machinery/computer/camera_advanced/xenobio/proc/XenoMonkeyClickCtrl(mob/living/user, mob/living/carbon/human/target_mob) + SIGNAL_HANDLER + if(!ismonkey(target_mob)) + return + + var/mob/camera/ai_eye/remote/xenobio/remote_eye = user.remote_control + var/obj/machinery/computer/camera_advanced/xenobio/xeno_console = remote_eye.origin + + if(!xeno_console.validate_area(user, remote_eye, target_mob.loc)) + return + + if(!xeno_console.connected_recycler) + to_chat(user, span_warning("There is no connected monkey recycler. Use a multitool to link one.")) + return + + xeno_console.monkey_recycle(user, target_mob) diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index 2bbefb64ec3..1ce7f080d0f 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -11,28 +11,32 @@ throw_speed = 3 throw_range = 6 grind_results = list() - var/Uses = 1 ///uses before it goes inert - var/qdel_timer = null ///deletion timer, for delayed reactions - var/effectmod ///Which type of crossbred - var/list/activate_reagents = list() ///Reagents required for activation + ///uses before it goes inert + var/extract_uses = 1 + ///deletion timer, for delayed reactions + var/qdel_timer = null + ///Which type of crossbred + var/crossbreed_modification + ///Reagents required for activation + var/list/activate_reagents = list() var/recurring = FALSE /obj/item/slime_extract/examine(mob/user) . = ..() - if(Uses > 1) - . += "It has [Uses] uses remaining." + if(extract_uses > 1) + . += "It has [extract_uses] uses remaining." /obj/item/slime_extract/attackby(obj/item/O, mob/user) if(istype(O, /obj/item/slimepotion/enhancer)) - if(Uses >= 5 || recurring) + if(extract_uses >= 5 || recurring) to_chat(user, span_warning("You cannot enhance this extract further!")) return ..() if(O.type == /obj/item/slimepotion/enhancer) //Seriously, why is this defined here...? to_chat(user, span_notice("You apply the enhancer to the slime extract. It may now be reused one more time.")) - Uses++ + extract_uses++ if(O.type == /obj/item/slimepotion/enhancer/max) to_chat(user, span_notice("You dump the maximizer on the slime extract. It can now be used a total of 5 times!")) - Uses = 5 + extract_uses = 5 qdel(O) ..() @@ -42,7 +46,7 @@ /obj/item/slime_extract/on_grind() . = ..() - if(Uses) + if(extract_uses) grind_results[/datum/reagent/toxin/slimejelly] = 20 /** @@ -63,34 +67,34 @@ * * By using a valid core on a living adult slime, then feeding it nine more of the same type, you can mutate it into more useful items. Not every slime type has an implemented core cross. */ -/obj/item/slime_extract/attack(mob/living/simple_animal/slime/M, mob/user) - if(!isslime(M)) +/obj/item/slime_extract/attack(mob/living/simple_animal/slime/target_slime, mob/user) + if(!isslime(target_slime)) return ..() - if(M.stat) + if(target_slime.stat) to_chat(user, span_warning("The slime is dead!")) return - if(!M.is_adult) + if(!target_slime.is_adult) to_chat(user, span_warning("The slime must be an adult to cross its core!")) return - if(M.effectmod && M.effectmod != effectmod) + if(target_slime.crossbreed_modification && target_slime.crossbreed_modification != crossbreed_modification) to_chat(user, span_warning("The slime is already being crossed with a different extract!")) return - if(!M.effectmod) - M.effectmod = effectmod + if(!target_slime.crossbreed_modification) + target_slime.crossbreed_modification = crossbreed_modification - M.applied++ + target_slime.applied_crossbreed_amount++ qdel(src) - to_chat(user, span_notice("You feed the slime [src], [M.applied == 1 ? "starting to mutate its core." : "further mutating its core."]")) - playsound(M, 'sound/effects/attackblob.ogg', 50, TRUE) + to_chat(user, span_notice("You feed the slime [src], [target_slime.applied_crossbreed_amount == 1 ? "starting to mutate its core." : "further mutating its core."]")) + playsound(target_slime, 'sound/effects/attackblob.ogg', 50, TRUE) - if(M.applied >= SLIME_EXTRACT_CROSSING_REQUIRED) - M.spawn_corecross() + if(target_slime.applied_crossbreed_amount >= SLIME_EXTRACT_CROSSING_REQUIRED) + target_slime.spawn_corecross() /obj/item/slime_extract/grey name = "grey slime extract" icon_state = "grey slime extract" - effectmod = "reproductive" + crossbreed_modification = "reproductive" activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,/datum/reagent/water) /obj/item/slime_extract/grey/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -105,7 +109,7 @@ if(SLIME_ACTIVATE_MAJOR) to_chat(user, span_notice("Your [name] starts pulsing...")) if(do_after(user, 40, target = user)) - var/mob/living/simple_animal/slime/S = new(get_turf(user), SLIME_TYPE_GREY) + var/mob/living/simple_animal/slime/S = new(get_turf(user), /datum/slime_type/grey) playsound(user, 'sound/effects/splat.ogg', 50, TRUE) to_chat(user, span_notice("You spit out [S].")) return 350 @@ -115,7 +119,7 @@ /obj/item/slime_extract/gold name = "gold slime extract" icon_state = "gold slime extract" - effectmod = "symbiont" + crossbreed_modification = "symbiont" activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,/datum/reagent/water) @@ -146,7 +150,7 @@ /obj/item/slime_extract/silver name = "silver slime extract" icon_state = "silver slime extract" - effectmod = "consuming" + crossbreed_modification = "consuming" activate_reagents = list(/datum/reagent/toxin/plasma,/datum/reagent/water) @@ -174,7 +178,7 @@ /obj/item/slime_extract/metal name = "metal slime extract" icon_state = "metal slime extract" - effectmod = "industrial" + crossbreed_modification = "industrial" activate_reagents = list(/datum/reagent/toxin/plasma,/datum/reagent/water) /obj/item/slime_extract/metal/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -198,7 +202,7 @@ /obj/item/slime_extract/purple name = "purple slime extract" icon_state = "purple slime extract" - effectmod = "regenerative" + crossbreed_modification = "regenerative" activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma) /obj/item/slime_extract/purple/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -217,7 +221,7 @@ /obj/item/slime_extract/darkpurple name = "dark purple slime extract" icon_state = "dark purple slime extract" - effectmod = "self-sustaining" + crossbreed_modification = "self-sustaining" activate_reagents = list(/datum/reagent/toxin/plasma) /obj/item/slime_extract/darkpurple/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -240,7 +244,7 @@ /obj/item/slime_extract/orange name = "orange slime extract" icon_state = "orange slime extract" - effectmod = "burning" + crossbreed_modification = "burning" activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,/datum/reagent/water) /obj/item/slime_extract/orange/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -260,7 +264,7 @@ /obj/item/slime_extract/yellow name = "yellow slime extract" icon_state = "yellow slime extract" - effectmod = "charged" + crossbreed_modification = "charged" activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,/datum/reagent/water) /obj/item/slime_extract/yellow/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -283,7 +287,7 @@ /obj/item/slime_extract/red name = "red slime extract" icon_state = "red slime extract" - effectmod = "sanguine" + crossbreed_modification = "sanguine" activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,/datum/reagent/water) /obj/item/slime_extract/red/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -303,7 +307,7 @@ /obj/item/slime_extract/blue name = "blue slime extract" icon_state = "blue slime extract" - effectmod = "stabilized" + crossbreed_modification = "stabilized" activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,/datum/reagent/water) /obj/item/slime_extract/blue/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -323,7 +327,7 @@ /obj/item/slime_extract/darkblue name = "dark blue slime extract" icon_state = "dark blue slime extract" - effectmod = "chilling" + crossbreed_modification = "chilling" activate_reagents = list(/datum/reagent/toxin/plasma,/datum/reagent/water) /obj/item/slime_extract/darkblue/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -346,7 +350,7 @@ /obj/item/slime_extract/pink name = "pink slime extract" icon_state = "pink slime extract" - effectmod = "gentle" + crossbreed_modification = "gentle" activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma) /obj/item/slime_extract/pink/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -374,7 +378,7 @@ /obj/item/slime_extract/green name = "green slime extract" icon_state = "green slime extract" - effectmod = "mutative" + crossbreed_modification = "mutative" activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,/datum/reagent/uranium/radium) /obj/item/slime_extract/green/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -398,7 +402,7 @@ /obj/item/slime_extract/lightpink name = "light pink slime extract" icon_state = "light pink slime extract" - effectmod = "loyal" + crossbreed_modification = "loyal" activate_reagents = list(/datum/reagent/toxin/plasma) /obj/item/slime_extract/lightpink/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -422,7 +426,7 @@ /obj/item/slime_extract/black name = "black slime extract" icon_state = "black slime extract" - effectmod = "transformative" + crossbreed_modification = "transformative" activate_reagents = list(/datum/reagent/toxin/plasma) /obj/item/slime_extract/black/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -443,7 +447,7 @@ /obj/item/slime_extract/oil name = "oil slime extract" icon_state = "oil slime extract" - effectmod = "detonating" + crossbreed_modification = "detonating" activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma) /obj/item/slime_extract/oil/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -467,7 +471,7 @@ /obj/item/slime_extract/adamantine name = "adamantine slime extract" icon_state = "adamantine slime extract" - effectmod = "crystalline" + crossbreed_modification = "crystalline" activate_reagents = list(/datum/reagent/toxin/plasma) /obj/item/slime_extract/adamantine/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -497,7 +501,7 @@ /obj/item/slime_extract/bluespace name = "bluespace slime extract" icon_state = "bluespace slime extract" - effectmod = "warping" + crossbreed_modification = "warping" activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma) var/teleport_ready = FALSE var/teleport_x = 0 @@ -533,7 +537,7 @@ /obj/item/slime_extract/pyrite name = "pyrite slime extract" icon_state = "pyrite slime extract" - effectmod = "prismatic" + crossbreed_modification = "prismatic" activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma) /obj/item/slime_extract/pyrite/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -560,7 +564,7 @@ /obj/item/slime_extract/cerulean name = "cerulean slime extract" icon_state = "cerulean slime extract" - effectmod = "recurring" + crossbreed_modification = "recurring" activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma) /obj/item/slime_extract/cerulean/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -580,7 +584,7 @@ /obj/item/slime_extract/sepia name = "sepia slime extract" icon_state = "sepia slime extract" - effectmod = "lengthened" + crossbreed_modification = "lengthened" activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,/datum/reagent/water) /obj/item/slime_extract/sepia/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -602,7 +606,7 @@ /obj/item/slime_extract/rainbow name = "rainbow slime extract" icon_state = "rainbow slime extract" - effectmod = "hyperchromatic" + crossbreed_modification = "hyperchromatic" activate_reagents = list(/datum/reagent/blood,/datum/reagent/toxin/plasma,"lesser plasma",/datum/reagent/toxin/slimejelly,"holy water and uranium") //Curse this snowflake reagent list. /obj/item/slime_extract/rainbow/activate(mob/living/carbon/human/user, datum/species/jelly/luminescent/species, activation_type) @@ -668,7 +672,7 @@ M.rabid = FALSE qdel(src) return - M.docile = 1 + M.docile = TRUE M.set_nutrition(700) to_chat(M, span_warning("You absorb the potion and feel your intense desire to feed melt away.")) to_chat(user, span_notice("You feed the slime the potion, removing its hunger and calming it.")) diff --git a/code/modules/surgery/core_removal.dm b/code/modules/surgery/core_removal.dm index 4ada9e7b59a..47f1e983f19 100644 --- a/code/modules/surgery/core_removal.dm +++ b/code/modules/surgery/core_removal.dm @@ -49,10 +49,10 @@ span_notice("[user] successfully extracts a core from [target]!"), ) - new target_slime.coretype(target_slime.loc) + new target_slime.slime_type.core_type(target_slime.loc) if(target_slime.cores <= 0) - target_slime.icon_state = "[target_slime.colour] baby slime dead-nocore" + target_slime.icon_state = "[target_slime.slime_type.colour] baby slime dead-nocore" return ..() else return FALSE diff --git a/tgstation.dme b/tgstation.dme index 2553b026ce6..9adb663bc48 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4907,7 +4907,7 @@ #include "code\modules\mob\living\simple_animal\slime\powers.dm" #include "code\modules\mob\living\simple_animal\slime\slime.dm" #include "code\modules\mob\living\simple_animal\slime\slime_say.dm" -#include "code\modules\mob\living\simple_animal\slime\subtypes.dm" +#include "code\modules\mob\living\simple_animal\slime\slime_type.dm" #include "code\modules\mob_spawn\mob_spawn.dm" #include "code\modules\mob_spawn\corpses\job_corpses.dm" #include "code\modules\mob_spawn\corpses\mining_corpses.dm" diff --git a/tools/UpdatePaths/Scripts/79852_slime_colour_renames.txt b/tools/UpdatePaths/Scripts/79852_slime_colour_renames.txt new file mode 100644 index 00000000000..a8e59fb6a88 --- /dev/null +++ b/tools/UpdatePaths/Scripts/79852_slime_colour_renames.txt @@ -0,0 +1,22 @@ + +/mob/living/simple_animal/slime{colour="grey"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/grey} +/mob/living/simple_animal/slime{colour="metal"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/blue} +/mob/living/simple_animal/slime{colour="purple"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/purple} +/mob/living/simple_animal/slime{colour="orange"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/orange} +/mob/living/simple_animal/slime{colour="dark blue"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/darkblue} +/mob/living/simple_animal/slime{colour="dark purple"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/darkpurple} +/mob/living/simple_animal/slime{colour="silver"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/silver} +/mob/living/simple_animal/slime{colour="yellow"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/yellow} +/mob/living/simple_animal/slime{colour="bluespace"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/bluespace} +/mob/living/simple_animal/slime{colour="cerulean"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/cerulean} +/mob/living/simple_animal/slime{colour="pyrite"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/pyrite} +/mob/living/simple_animal/slime{colour="sepia"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/sepia} +/mob/living/simple_animal/slime{colour="gold"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/gold} +/mob/living/simple_animal/slime{colour="green"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/green} +/mob/living/simple_animal/slime{colour="pick"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/pick} +/mob/living/simple_animal/slime{colour="red"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/red} +/mob/living/simple_animal/slime{colour="adamantine"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/adamantine} +/mob/living/simple_animal/slime{colour="black"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/black} +/mob/living/simple_animal/slime{colour="light pink"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/lightpink} +/mob/living/simple_animal/slime{colour="oil"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/oil} +/mob/living/simple_animal/slime{colour="rainbow"} : /mob/living/simple_animal/slime{@OLD;colour=@SKIP;slime_type=/datum/slime_type/rainbow} \ No newline at end of file