diff --git a/code/__DEFINES/research/slimes.dm b/code/__DEFINES/research/slimes.dm
index 64a85afc217..59410564928 100644
--- a/code/__DEFINES/research/slimes.dm
+++ b/code/__DEFINES/research/slimes.dm
@@ -1,7 +1,13 @@
+#define SLIME_LIFE_STAGE_BABY "baby"
+#define SLIME_LIFE_STAGE_ADULT "adult"
+
+#define SLIME_MIN_POWER 0
+#define SLIME_MEDIUM_POWER 5
+#define SLIME_MAX_POWER 10
+
// Just slimin' here.
-// Warning: These defines are used for slime cores and their icon states, so if you
-// touch these names, remember to update icons/mob/simple/slimes.dmi and the respective
-// slime core paths too!
+// Warning: These defines are used for slime icon states, so if you
+// touch these names, remember to update icons/mob/simple/slimes.dmi!
#define SLIME_TYPE_ADAMANTINE "adamantine"
#define SLIME_TYPE_BLACK "black"
diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm
index f398a5c0343..03d6606a5f0 100644
--- a/code/_onclick/other_mobs.dm
+++ b/code/_onclick/other_mobs.dm
@@ -237,31 +237,6 @@
/atom/proc/attack_larva_secondary(mob/user, list/modifiers)
return SECONDARY_ATTACK_CALL_NORMAL
-
-/*
- Slimes
- Nothing happening here
-*/
-/mob/living/simple_animal/slime/resolve_unarmed_attack(atom/attack_target, proximity_flag, list/modifiers)
- if(isturf(attack_target))
- return ..()
- attack_target.attack_slime(src, modifiers)
-
-/mob/living/simple_animal/slime/resolve_right_click_attack(atom/target, list/modifiers)
- if(isturf(target))
- return ..()
- return target.attack_slime_secondary(src, modifiers)
-
-/atom/proc/attack_slime(mob/user, list/modifiers)
- return
-
-/**
- * Called when a slime mob right clicks an atom (that is not a turf).
- * Returns a SECONDARY_ATTACK_* value.
- */
-/atom/proc/attack_slime_secondary(mob/user, list/modifiers)
- return SECONDARY_ATTACK_CALL_NORMAL
-
/*
Drones
*/
diff --git a/code/game/atom/atom_defense.dm b/code/game/atom/atom_defense.dm
index 0541915a38c..e18b3ade2f1 100644
--- a/code/game/atom/atom_defense.dm
+++ b/code/game/atom/atom_defense.dm
@@ -121,7 +121,7 @@
/atom/proc/hulk_damage()
return 150 //the damage hulks do on punches to this atom, is affected by melee armor
-/atom/proc/attack_generic(mob/user, damage_amount = 0, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, armor_penetration = 0) //used by attack_alien, attack_animal, and attack_slime
+/atom/proc/attack_generic(mob/user, damage_amount = 0, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, armor_penetration = 0) //used by attack_alien, attack_animal
if(!uses_integrity)
CRASH("unimplemented /atom/proc/attack_generic()!")
user.do_attack_animation(src)
diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm
index 75623ed57e8..e5507473fae 100644
--- a/code/game/objects/items/devices/chameleonproj.dm
+++ b/code/game/objects/items/devices/chameleonproj.dm
@@ -140,9 +140,6 @@
/obj/effect/dummy/chameleon/attack_animal(mob/user, list/modifiers)
master.disrupt()
-/obj/effect/dummy/chameleon/attack_slime(mob/user, list/modifiers)
- master.disrupt()
-
/obj/effect/dummy/chameleon/attack_alien(mob/user, list/modifiers)
master.disrupt()
diff --git a/code/game/objects/items/devices/scanners/slime_scanner.dm b/code/game/objects/items/devices/scanners/slime_scanner.dm
index 133dfee0b87..05b11b3be3a 100644
--- a/code/game/objects/items/devices/scanners/slime_scanner.dm
+++ b/code/game/objects/items/devices/scanners/slime_scanner.dm
@@ -27,12 +27,12 @@
/proc/slime_scan(mob/living/simple_animal/slime/scanned_slime, mob/living/user)
var/to_render = "Slime scan results:\
- \n[span_notice("[scanned_slime.slime_type.colour] [scanned_slime.is_adult ? "adult" : "baby"] slime")]\
- \nNutrition: [scanned_slime.nutrition]/[scanned_slime.get_max_nutrition()]"
+ \n[span_notice("[scanned_slime.slime_type.colour] [scanned_slime.life_stage] slime")]\
+ \nNutrition: [scanned_slime.nutrition]/[scanned_slime.max_nutrition]"
- if (scanned_slime.nutrition < scanned_slime.get_starve_nutrition())
+ if (scanned_slime.nutrition < scanned_slime.starve_nutrition)
to_render += "\n[span_warning("Warning: slime is starving!")]"
- else if (scanned_slime.nutrition < scanned_slime.get_hunger_nutrition())
+ else if (scanned_slime.nutrition < scanned_slime.hunger_nutrition)
to_render += "\n[span_warning("Warning: slime is hungry")]"
to_render += "\nElectric charge strength: [scanned_slime.powerlevel]\nHealth: [round(scanned_slime.health/scanned_slime.maxHealth,0.01)*100]%"
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index 8b608427e21..a2989ad1444 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -98,12 +98,6 @@
var/amt = max(0, ((force - (move_resist * MOVE_FORCE_CRUSH_RATIO)) / (move_resist * MOVE_FORCE_CRUSH_RATIO)) * 10)
take_damage(amt, BRUTE)
-/obj/attack_slime(mob/living/simple_animal/slime/user, list/modifiers)
- if(!user.is_adult)
- return
- if(attack_generic(user, rand(10, 15), BRUTE, MELEE, 1))
- log_combat(user, src, "attacked")
-
/obj/singularity_act()
SSexplosions.high_mov_atom += src
if(src && !QDELETED(src))
diff --git a/code/game/objects/structures/ladders.dm b/code/game/objects/structures/ladders.dm
index 9b7b5590018..ef6ea9d433e 100644
--- a/code/game/objects/structures/ladders.dm
+++ b/code/game/objects/structures/ladders.dm
@@ -224,17 +224,6 @@
use(user, going_up = FALSE)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-/obj/structure/ladder/attack_slime(mob/user, list/modifiers)
- use(user)
- return TRUE
-
-/obj/structure/ladder/attack_slime_secondary(mob/user, list/modifiers)
- . = ..()
- if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
- return
- use(user, going_up = FALSE)
- return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-
/obj/structure/ladder/attackby(obj/item/item, mob/user, params)
use(user)
return TRUE
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 1b2e37cbc95..ad969543416 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -185,7 +185,7 @@
/obj/structure/window/attack_paw(mob/user, list/modifiers)
return attack_hand(user, modifiers)
-/obj/structure/window/attack_generic(mob/user, damage_amount = 0, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) //used by attack_alien, attack_animal, and attack_slime
+/obj/structure/window/attack_generic(mob/user, damage_amount = 0, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) //used by attack_alien, attack_animal
if(!can_be_reached(user))
return
return ..()
diff --git a/code/modules/awaymissions/signpost.dm b/code/modules/awaymissions/signpost.dm
index c46b0b9aa3c..864b4fa03f8 100644
--- a/code/modules/awaymissions/signpost.dm
+++ b/code/modules/awaymissions/signpost.dm
@@ -46,9 +46,6 @@
if (Adjacent(user))
return interact(user)
-/obj/structure/signpost/attack_slime(mob/user, list/modifiers)
- return interact(user)
-
/obj/structure/signpost/attack_animal(mob/user, list/modifiers)
return interact(user)
diff --git a/code/modules/awaymissions/super_secret_room.dm b/code/modules/awaymissions/super_secret_room.dm
index 556a9fd63ad..d7ecda85018 100644
--- a/code/modules/awaymissions/super_secret_room.dm
+++ b/code/modules/awaymissions/super_secret_room.dm
@@ -105,8 +105,6 @@
/obj/structure/speaking_tile/attack_ai(mob/user)
return interact(user)
-/obj/structure/speaking_tile/attack_slime(mob/user, list/modifiers)
- return interact(user)
/obj/structure/speaking_tile/attack_animal(mob/user, list/modifiers)
return interact(user)
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm b/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm
index 05daaff5864..cf5ad9d324c 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm
@@ -335,9 +335,6 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999))
/turf/closed/indestructible/hoteldoor/attack_larva(mob/user, list/modifiers)
promptExit(user)
-/turf/closed/indestructible/hoteldoor/attack_slime(mob/user, list/modifiers)
- promptExit(user)
-
/turf/closed/indestructible/hoteldoor/attack_robot(mob/user)
if(get_dist(get_turf(src), get_turf(user)) <= 1)
promptExit(user)
diff --git a/code/modules/mob/living/basic/basic_defense.dm b/code/modules/mob/living/basic/basic_defense.dm
index 03f684b7d87..69370ed49be 100644
--- a/code/modules/mob/living/basic/basic_defense.dm
+++ b/code/modules/mob/living/basic/basic_defense.dm
@@ -112,13 +112,6 @@
if(damage_done > 0)
attacking_larva.amount_grown = min(attacking_larva.amount_grown + damage_done, attacking_larva.max_grown)
-/mob/living/basic/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
- if(..()) //successful slime attack
- var/damage = rand(15, 25)
- if(M.is_adult)
- damage = rand(20, 35)
- return apply_damage(damage, M.melee_damage_type)
-
/mob/living/basic/attack_drone(mob/living/basic/drone/attacking_drone)
if(attacking_drone.combat_mode) //No kicking dogs even as a rogue drone. Use a weapon.
return
diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm
index 4ea7e98b05f..9388dcea560 100644
--- a/code/modules/mob/living/carbon/alien/alien_defense.dm
+++ b/code/modules/mob/living/carbon/alien/alien_defense.dm
@@ -67,15 +67,6 @@ In all, this is a lot like the monkey code. /N
var/obj/item/bodypart/affecting = get_bodypart(get_random_valid_zone(user.zone_selected))
apply_damage(rand(1, 3), BRUTE, affecting)
-/mob/living/carbon/alien/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
- if(..()) //successful slime attack
- var/damage = rand(5, 35)
- if(M.is_adult)
- damage = rand(10, 40)
- adjustBruteLoss(damage)
- log_combat(M, src, "attacked")
- updatehealth()
-
/mob/living/carbon/alien/ex_act(severity, target, origin)
. = ..()
if(!. || QDELETED(src))
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index cb565324c67..a2faacb9a94 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -202,28 +202,6 @@
ForceContractDisease(D)
return TRUE
-
-/mob/living/carbon/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
- if(..()) //successful slime attack
- if(M.powerlevel > 0)
- var/stunprob = M.powerlevel * 7 + 10 // 17 at level 1, 80 at level 10
- if(prob(stunprob))
- M.powerlevel -= 3
- if(M.powerlevel < 0)
- M.powerlevel = 0
-
- visible_message(span_danger("The [M.name] shocks [src]!"), \
- span_userdanger("The [M.name] shocks you!"))
-
- do_sparks(5, TRUE, src)
- var/power = M.powerlevel + rand(0,3)
- Paralyze(power * 2 SECONDS)
- set_stutter_if_lower(power * 2 SECONDS)
- if (prob(stunprob) && M.powerlevel >= 8)
- adjustFireLoss(M.powerlevel * rand(6,10))
- updatehealth()
- return 1
-
/**
* Really weird proc that attempts to dismebmer the passed zone if it is at max damage
* Unless the attacker is an NPC, in which case it disregards the zone and picks a random one
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index bb128182faa..094a812a0ab 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -254,30 +254,6 @@
var/armor_block = run_armor_check(affecting, MELEE)
apply_damage(damage, BRUTE, affecting, armor_block)
-/mob/living/carbon/human/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
- . = ..()
- if(!.) // slime attack failed
- return
- var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
- if(!damage)
- return
- var/wound_mod = -45 // 25^1.4=90, 90-45=45
- if(M.is_adult)
- damage += rand(5, 10)
- wound_mod = -90 // 35^1.4=145, 145-90=55
-
- if(check_block(M, damage, "the [M.name]"))
- return FALSE
-
- var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
- if(!dam_zone) //Dismemberment successful
- return TRUE
-
- var/obj/item/bodypart/affecting = get_bodypart(get_random_valid_zone(dam_zone))
- var/armor_block = run_armor_check(affecting, MELEE)
- apply_damage(damage, BRUTE, affecting, armor_block, wound_bonus=wound_mod)
-
-
/mob/living/carbon/human/ex_act(severity, target, origin)
if(HAS_TRAIT(src, TRAIT_BOMBIMMUNE))
return FALSE
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 3138f4451fd..2cc5c14b172 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -284,30 +284,6 @@
user.set_pull_offsets(src, grab_state)
return TRUE
-
-/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(attacking_slime, TRAIT_PACIFISM))
- to_chat(attacking_slime, span_warning("You don't want to hurt anyone!"))
- return FALSE
-
- 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(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
-
/mob/living/attack_animal(mob/living/simple_animal/user, list/modifiers)
. = ..()
if(.)
diff --git a/code/modules/mob/living/silicon/ai/ai_defense.dm b/code/modules/mob/living/silicon/ai/ai_defense.dm
index da8411f7cb3..1148b639d01 100644
--- a/code/modules/mob/living/silicon/ai/ai_defense.dm
+++ b/code/modules/mob/living/silicon/ai/ai_defense.dm
@@ -10,9 +10,6 @@
return ..()
-/mob/living/silicon/ai/attack_slime(mob/living/simple_animal/slime/user, list/modifiers)
- return //immune to slimes
-
/mob/living/silicon/ai/blob_act(obj/structure/blob/B)
if (stat != DEAD)
adjustBruteLoss(60)
diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm
index 074f9d0049a..44e8583b86f 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -207,24 +207,6 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
..()
return
-/mob/living/silicon/robot/attack_slime(mob/living/simple_animal/slime/M, list/modifiers)
- if(..()) //successful slime shock
- flash_act()
- var/stunprob = M.powerlevel * 7 + 10
- if(prob(stunprob) && M.powerlevel >= 8)
- adjustBruteLoss(M.powerlevel * rand(6,10))
-
- var/damage = rand(1, 3)
-
- if(M.is_adult)
- damage = rand(20, 40)
- else
- damage = rand(5, 35)
- damage = round(damage / 2) // borgs receive half damage
- adjustBruteLoss(damage)
-
- return
-
/mob/living/silicon/robot/attack_hand(mob/living/carbon/human/user, list/modifiers)
add_fingerprint(user)
if(!opened)
diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm
index 01d144aea88..afe4c00db4d 100644
--- a/code/modules/mob/living/simple_animal/animal_defense.dm
+++ b/code/modules/mob/living/simple_animal/animal_defense.dm
@@ -91,13 +91,6 @@
if(damage_done > 0)
L.amount_grown = min(L.amount_grown + damage_done, L.max_grown)
-/mob/living/simple_animal/attack_slime(mob/living/simple_animal/slime/user, list/modifiers)
- if(..()) //successful slime attack
- var/damage = rand(15, 25)
- if(user.is_adult)
- damage = rand(20, 35)
- return apply_damage(damage, user.melee_damage_type)
-
/mob/living/simple_animal/attack_drone(mob/living/basic/drone/user)
if(user.combat_mode) //No kicking dogs even as a rogue drone. Use a weapon.
return
diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm
index a3d56af8b89..3a838ac58cd 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/curse_blob.dm
@@ -119,8 +119,6 @@ IGNORE_PROC_IF_NOT_TARGET(attack_larva)
IGNORE_PROC_IF_NOT_TARGET(attack_animal)
-IGNORE_PROC_IF_NOT_TARGET(attack_slime)
-
/mob/living/simple_animal/hostile/asteroid/curseblob/bullet_act(obj/projectile/Proj)
if(Proj.firer != set_target)
return
diff --git a/code/modules/mob/living/simple_animal/slime/ai.dm b/code/modules/mob/living/simple_animal/slime/ai.dm
new file mode 100644
index 00000000000..0272a4e467b
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/slime/ai.dm
@@ -0,0 +1,494 @@
+#define SLIME_CARES_ABOUT(to_check) (to_check && (to_check == Target || to_check == Leader || (to_check in Friends)))
+#define SLIME_HUNGER_NONE 0
+#define SLIME_HUNGER_HUNGRY 1
+#define SLIME_HUNGER_STARVING 2
+
+/mob/living/simple_animal/slime/handle_automated_movement()
+ return //slime random movement is currently handled in handle_targets()
+
+/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)
+ #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 = SLIME_MOOD_SMILE
+ else if (Target)
+ newmood = SLIME_MOOD_MISCHIEVOUS
+
+ if (!newmood)
+ if (discipline_stacks && SPT_PROB(13, seconds_per_tick))
+ newmood = SLIME_MOOD_POUT
+ else if (SPT_PROB(0.5, seconds_per_tick))
+ newmood = pick(SLIME_MOOD_SAD, ":3", SLIME_MOOD_POUT)
+
+ 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
+
+ if (newmood != current_mood) // This is so we don't redraw them every time
+ 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(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"))
+ if (Leader)
+ if (Leader == who) // Already following him
+ to_say = pick("Yes...", "Lead...", "Follow...")
+ else if (Friends[who] > Friends[Leader]) // VIVA
+ set_leader(who)
+ to_say = "Yes... I follow [who]..."
+ else
+ to_say = "No... I follow [Leader]..."
+ else
+ if (Friends[who] >= SLIME_FRIENDSHIP_FOLLOW)
+ set_leader(who)
+ to_say = "I follow..."
+ else // Not friendly enough
+ to_say = pick("No...", "I no follow...")
+ else if (findtext(phrase, "stop"))
+ if (buckled) // We are asked to stop feeding
+ if (Friends[who] >= SLIME_FRIENDSHIP_STOPEAT)
+ stop_feeding()
+ set_target(null)
+ if (Friends[who] < SLIME_FRIENDSHIP_STOPEAT_NOANGRY)
+ add_friendship(who, -1)
+ to_say = "Grrr..." // I'm angry but I do it
+ else
+ to_say = "Fine..."
+ else if (Target) // We are asked to stop chasing
+ if (Friends[who] >= SLIME_FRIENDSHIP_STOPCHASE)
+ set_target(null)
+ if (Friends[who] < SLIME_FRIENDSHIP_STOPCHASE_NOANGRY)
+ add_friendship(who, -1)
+ to_say = "Grrr..." // I'm angry but I do it
+ else
+ to_say = "Fine..."
+ else if (Leader) // We are asked to stop following
+ if (Leader == who)
+ to_say = "Yes... I stay..."
+ set_leader(null)
+ else
+ if (Friends[who] > Friends[Leader])
+ set_leader(null)
+ to_say = "Yes... I stop..."
+ else
+ to_say = "No... keep follow..."
+ else if (findtext(phrase, "stay"))
+ if (Leader)
+ if (Leader == who)
+ holding_still = Friends[who] * 10
+ to_say = "Yes... stay..."
+ else if (Friends[who] > Friends[Leader])
+ holding_still = (Friends[who] - Friends[Leader]) * 10
+ to_say = "Yes... stay..."
+ else
+ to_say = "No... keep follow..."
+ else
+ if (Friends[who] >= SLIME_FRIENDSHIP_STAY)
+ holding_still = Friends[who] * 10
+ to_say = "Yes... stay..."
+ else
+ to_say = "No... won't stay..."
+ else if (findtext(phrase, "attack"))
+ if (rabid && prob(20))
+ set_target(who)
+ 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/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[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 [possible_target] ..."
+ add_friendship(who, -1) //Don't ask a slime to attack its friend
+ break
+ else
+ to_say = "No... no listen"
+
+ speech_buffer = list()
+
+ //Speech starts here
+ if (to_say)
+ say (to_say)
+ else if(SPT_PROB(0.5, seconds_per_tick))
+ emote(pick("bounce","sway","light","vibrate","jiggle"))
+ else
+ var/speech_chance = 10
+ var/slimes_near = 0
+ var/dead_slimes = 0
+ var/friends_near = list()
+ for (var/mob/living/seen_mob in view(7,src))
+ if(isslime(seen_mob) && seen_mob != src)
+ ++slimes_near
+ if (seen_mob.stat == DEAD)
+ ++dead_slimes
+ if (seen_mob in Friends)
+ speech_chance += 20
+ friends_near += seen_mob
+ if (nutrition < hunger_nutrition)
+ speech_chance += 10
+ if (nutrition < starve_nutrition)
+ speech_chance += 10
+ if (SPT_PROB(1, seconds_per_tick) && prob(speech_chance))
+ var/phrases = list()
+ if (Target)
+ phrases += "[Target]... look yummy..."
+ if (nutrition < starve_nutrition)
+ phrases += "So... hungry..."
+ phrases += "Very... hungry..."
+ phrases += "Need... food..."
+ phrases += "Must... eat..."
+ else if (nutrition < hunger_nutrition)
+ phrases += "Hungry..."
+ phrases += "Where food?"
+ phrases += "I want to eat..."
+ phrases += "Rawr..."
+ phrases += "Blop..."
+ phrases += "Blorble..."
+ if (rabid || attacked_stacks)
+ phrases += "Hrr..."
+ phrases += "Nhuu..."
+ phrases += "Unn..."
+ if (current_mood == ":3")
+ phrases += "Purr..."
+ if (attacked_stacks)
+ phrases += "Grrr..."
+ if (bodytemperature < T0C)
+ phrases += "Cold..."
+ if (bodytemperature < T0C - 30)
+ phrases += "So... cold..."
+ phrases += "Very... cold..."
+ if (bodytemperature < T0C - 50)
+ phrases += "..."
+ phrases += "C... c..."
+ if (buckled)
+ phrases += "Nom..."
+ phrases += "Yummy..."
+ if (powerlevel > 3)
+ phrases += "Bzzz..."
+ if (powerlevel > 5)
+ phrases += "Zap..."
+ if (powerlevel > 8)
+ phrases += "Zap... Bzz..."
+ if (current_mood == "sad")
+ phrases += "Bored..."
+ if (slimes_near)
+ phrases += "Slime friend..."
+ if (slimes_near > 1)
+ phrases += "Slime friends..."
+ if (dead_slimes)
+ phrases += "What happened?"
+ if (!slimes_near)
+ phrases += "Lonely..."
+ for (var/friend in friends_near)
+ phrases += "[friend]... friend..."
+ if (nutrition < hunger_nutrition)
+ phrases += "[friend]... feed me..."
+ if(!stat)
+ say (pick(phrases))
+
+///Sets the slime's current attack target
+/mob/living/simple_animal/slime/proc/set_target(new_target)
+ var/old_target = Target
+ Target = new_target
+ if(old_target && !SLIME_CARES_ABOUT(old_target))
+ UnregisterSignal(old_target, COMSIG_QDELETING)
+ 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
+ if(old_leader && !SLIME_CARES_ABOUT(old_leader))
+ UnregisterSignal(old_leader, COMSIG_QDELETING)
+ 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
+ Friends[new_friend] += amount
+ 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)
+ set_target(null)
+ if(source == Leader)
+ set_leader(null)
+ remove_friend(source)
+
+///Handles selecting targets
+/mob/living/simple_animal/slime/proc/handle_targets(seconds_per_tick, times_fired)
+ if(attacked_stacks > 50)
+ attacked_stacks = 50
+
+ if(attacked_stacks > 0)
+ attacked_stacks--
+
+ if(discipline_stacks > 0)
+
+ if(discipline_stacks >= 5 && rabid)
+ if(SPT_PROB(37, seconds_per_tick))
+ rabid = FALSE
+
+ if(SPT_PROB(5, seconds_per_tick))
+ discipline_stacks--
+
+ if(client) //player controlled slimes can decide for themselves
+ return
+
+ if(!(mobility_flags & MOBILITY_MOVE))
+ return
+
+ if(buckled)
+ return // if it's eating someone already, continue eating!
+
+ 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)
+
+ if(slime_ai_processing && stunned_until > world.time)
+ return
+
+ var/hungry = SLIME_HUNGER_NONE // determines if the slime is hungry
+
+ if (nutrition < starve_nutrition)
+ hungry = SLIME_HUNGER_STARVING
+ else if (nutrition < grow_nutrition && SPT_PROB(13, seconds_per_tick) || nutrition < hunger_nutrition)
+ hungry = SLIME_HUNGER_HUNGRY
+
+ 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)
+
+ 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()
+
+ 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(locate(/mob/living/simple_animal/slime) in L.buckled_mobs) // Only one slime can latch on at a time.
+ continue
+
+ 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(islarva(C) || ismonkey(C))
+ set_target(C)
+ break
+
+ if (Target)
+ target_patience = rand(5, 7)
+ if (life_stage == SLIME_LIFE_STAGE_ADULT)
+ target_patience += 3
+
+ 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(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))
+
+/// Check for being stopped from feeding and chasing
+/mob/living/simple_animal/slime/proc/will_hunt(hunger = -1)
+ if (docile)
+ return FALSE
+ if (hunger == SLIME_HUNGER_STARVING || rabid || attacked_stacks)
+ return TRUE
+ if (Leader)
+ return FALSE
+ if (holding_still)
+ return FALSE
+ return TRUE
+
+/// the master AI process
+/mob/living/simple_animal/slime/proc/process_slime_ai()
+
+ if(slime_ai_processing || stat || client)
+ return
+
+ var/hungry = SLIME_HUNGER_NONE
+ if (nutrition < starve_nutrition)
+ hungry = SLIME_HUNGER_STARVING
+ else if (nutrition < grow_nutrition && prob(25) || nutrition < hunger_nutrition)
+ hungry = SLIME_HUNGER_HUNGRY
+
+ slime_ai_processing = TRUE
+
+ 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
+
+ if(!Target || client)
+ break
+
+ if(Target.health <= -70 || Target.stat == DEAD)
+ set_target(null)
+ slime_ai_processing = FALSE
+ break
+
+ if(Target)
+ if(locate(/mob/living/simple_animal/slime) in Target.buckled_mobs)
+ set_target(null)
+ slime_ai_processing = FALSE
+ break
+ if(!slime_ai_processing)
+ break
+
+ if(Target in view(1,src))
+ 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_animal(src)
+ break
+ if((Target.body_position == STANDING_UP) && prob(80))
+
+ if(Target.client && Target.health >= 20)
+ 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_animal(src)
+
+ else
+ if(!is_attack_on_cooldown && Target.Adjacent(src))
+ start_feeding(Target)
+
+ else
+ if(!is_attack_on_cooldown && Target.Adjacent(src))
+ start_feeding(Target)
+
+ else if(Target in view(7, src))
+ if(!Target.Adjacent(src))
+ // Bug of the month candidate: slimes were attempting to move to target only if it was directly next to them, which caused them to target things, but not approach them
+ step_to(src, Target)
+ else
+ set_target(null)
+ slime_ai_processing = FALSE
+ break
+
+ var/sleeptime = cached_multiplicative_slowdown
+ if(sleeptime <= 0)
+ sleeptime = 1
+
+ sleep(sleeptime + 2) // this is about as fast as a player slime can go
+
+ slime_ai_processing = FALSE
+
+#undef SLIME_CARES_ABOUT
+
+#undef SLIME_HUNGER_NONE
+#undef SLIME_HUNGER_HUNGRY
+#undef SLIME_HUNGER_STARVING
diff --git a/code/modules/mob/living/simple_animal/slime/death.dm b/code/modules/mob/living/simple_animal/slime/death.dm
index 033d7d2414e..a072299d663 100644
--- a/code/modules/mob/living/simple_animal/slime/death.dm
+++ b/code/modules/mob/living/simple_animal/slime/death.dm
@@ -1,22 +1,17 @@
/mob/living/simple_animal/slime/death(gibbed)
if(stat == DEAD)
return
- if(!gibbed)
- if(is_adult)
- var/mob/living/simple_animal/slime/new_slime = new(drop_location(), slime_type.type)
- new_slime.rabid = TRUE
- new_slime.regenerate_icons()
+ if(!gibbed && life_stage == SLIME_LIFE_STAGE_ADULT)
+ 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/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
+ //revives us as a baby
+ set_life_stage(SLIME_LIFE_STAGE_BABY)
+ revive(HEAL_ALL)
+ regenerate_icons()
+ update_name()
+ return
if(buckled)
stop_feeding(silent = TRUE) //releases ourselves from the mob we fed on.
diff --git a/code/modules/mob/living/simple_animal/slime/defense.dm b/code/modules/mob/living/simple_animal/slime/defense.dm
new file mode 100644
index 00000000000..dd94e4e2bab
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/slime/defense.dm
@@ -0,0 +1,131 @@
+
+/mob/living/simple_animal/slime/adjustFireLoss(amount, updating_health = TRUE, forced = FALSE, required_bodytype)
+ if(!forced)
+ amount = -abs(amount)
+ return ..() //Heals them
+
+/mob/living/simple_animal/slime/emp_act(severity)
+ . = ..()
+ if(. & EMP_PROTECT_SELF)
+ return
+ powerlevel = 0 // oh no, the power!
+
+/mob/living/simple_animal/slime/attack_animal(mob/living/simple_animal/user, list/modifiers)
+ . = ..()
+ if(.)
+ attacked_stacks += 10
+
+/mob/living/simple_animal/slime/attack_paw(mob/living/carbon/human/user, list/modifiers)
+ if(..()) //successful monkey bite.
+ attacked_stacks += 10
+
+/mob/living/simple_animal/slime/attack_larva(mob/living/carbon/alien/larva/L, list/modifiers)
+ if(..()) //successful larva bite.
+ attacked_stacks += 10
+
+/mob/living/simple_animal/slime/attack_hulk(mob/living/carbon/human/user)
+ . = ..()
+ if(!.)
+ return
+ discipline_slime(user)
+
+/mob/living/simple_animal/slime/attack_hand(mob/living/carbon/human/user, list/modifiers)
+ if(buckled)
+ user.do_attack_animation(src, ATTACK_EFFECT_DISARM)
+ if(buckled == user)
+ if(prob(60))
+ user.visible_message(span_warning("[user] attempts to wrestle \the [name] off!"), \
+ span_danger("You attempt to wrestle \the [name] off!"))
+ playsound(loc, 'sound/weapons/punchmiss.ogg', 25, TRUE, -1)
+
+ else
+ user.visible_message(span_warning("[user] manages to wrestle \the [name] off!"), \
+ span_notice("You manage to wrestle \the [name] off!"))
+ playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+
+ discipline_slime(user)
+
+ else
+ if(prob(30))
+ buckled.visible_message(span_warning("[user] attempts to wrestle \the [name] off of [buckled]!"), \
+ span_warning("[user] attempts to wrestle \the [name] off of you!"))
+ playsound(loc, 'sound/weapons/punchmiss.ogg', 25, TRUE, -1)
+
+ else
+ buckled.visible_message(span_warning("[user] manages to wrestle \the [name] off of [buckled]!"), \
+ span_notice("[user] manage to wrestle \the [name] off of you!"))
+ playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+
+ discipline_slime(user)
+ else
+ if(stat == DEAD && surgeries.len)
+ 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(..()) //successful attack
+ 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_stacks += 10
+ discipline_slime(user)
+
+/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(attacking_item, /obj/item/stack/sheet/mineral/plasma) && !stat) //Lets 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/sheet = attacking_item
+ sheet.use(1)
+ return
+ 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("[attacking_item] passes right through [src]!"))
+ return
+ if(discipline_stacks && prob(50)) // wow, buddy, why am I getting attacked??
+ discipline_stacks = 0
+ if(attacking_item.force >= 3)
+ var/force_effect = attacking_item.force * (life_stage == SLIME_LIFE_STAGE_BABY ? 2 : 1)
+ if(prob(10 + force_effect))
+ discipline_slime(user)
+
+ 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
diff --git a/code/modules/mob/living/simple_animal/slime/life.dm b/code/modules/mob/living/simple_animal/slime/life.dm
index e17fd09e0fa..be158abaed5 100644
--- a/code/modules/mob/living/simple_animal/slime/life.dm
+++ b/code/modules/mob/living/simple_animal/slime/life.dm
@@ -1,6 +1,4 @@
-#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))
@@ -34,84 +32,6 @@
return
return ..()
-/// the master AI process
-/mob/living/simple_animal/slime/proc/process_slime_ai()
-
- if(slime_ai_processing || stat || client)
- return
-
- var/hungry = SLIME_HUNGER_NONE
- if (nutrition < get_starve_nutrition())
- hungry = SLIME_HUNGER_STARVING
- else if (nutrition < get_grow_nutrition() && prob(25) || nutrition < get_hunger_nutrition())
- hungry = SLIME_HUNGER_HUNGRY
-
- slime_ai_processing = TRUE
-
- 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
-
- if(!Target || client)
- break
-
- if(Target.health <= -70 || Target.stat == DEAD)
- set_target(null)
- slime_ai_processing = FALSE
- break
-
- if(Target)
- if(locate(/mob/living/simple_animal/slime) in Target.buckled_mobs)
- set_target(null)
- slime_ai_processing = FALSE
- break
- if(!slime_ai_processing)
- break
-
- if(Target in view(1,src))
- 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)
- break
- if((Target.body_position == STANDING_UP) && prob(80))
-
- if(Target.client && Target.health >= 20)
- 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(!is_attack_on_cooldown && Target.Adjacent(src))
- start_feeding(Target)
-
- else
- if(!is_attack_on_cooldown && Target.Adjacent(src))
- start_feeding(Target)
-
- else if(Target in view(7, src))
- if(!Target.Adjacent(src))
- // Bug of the month candidate: slimes were attempting to move to target only if it was directly next to them, which caused them to target things, but not approach them
- step_to(src, Target)
- else
- set_target(null)
- slime_ai_processing = FALSE
- break
-
- var/sleeptime = cached_multiplicative_slowdown
- if(sleeptime <= 0)
- sleeptime = 1
-
- sleep(sleeptime + 2) // this is about as fast as a player slime can go
-
- 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)
var/divisor = 10 /// The divisor controls how fast body temperature changes, lower causes faster changes
@@ -209,7 +129,7 @@
if(need_mob_update)
animal_victim.updatehealth()
- if(totaldamage <= 0) //if we did no(or negative!) damage to it, stop
+ if(totaldamage >= 0) // AdjustBruteLoss returns a negative value on succesful damage adjustment
stop_feeding(FALSE, FALSE)
return
@@ -230,411 +150,32 @@
return
if(SPT_PROB(7.5, seconds_per_tick))
- adjust_nutrition(-0.5 * (1 + is_adult) * seconds_per_tick)
+ adjust_nutrition((life_stage == SLIME_LIFE_STAGE_ADULT ? -1 : -0.5) * seconds_per_tick)
if(nutrition <= 0)
set_nutrition(0)
if(SPT_PROB(50, seconds_per_tick))
adjustBruteLoss(rand(0,5))
- else if (nutrition >= get_grow_nutrition() && amount_grown < SLIME_EVOLUTION_THRESHOLD)
+ else if (nutrition >= grow_nutrition && amount_grown < SLIME_EVOLUTION_THRESHOLD)
adjust_nutrition(-10 * seconds_per_tick)
amount_grown++
update_mob_action_buttons()
if(amount_grown >= SLIME_EVOLUTION_THRESHOLD && !buckled && !Target && !ckey)
- if(is_adult && loc.AllowDrop())
+ if(life_stage == SLIME_LIFE_STAGE_ADULT && loc.AllowDrop())
Reproduce()
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())
- if(powerlevel<10)
+ set_nutrition(min((nutrition + nutrition_to_add), max_nutrition))
+ if(nutrition >= grow_nutrition)
+ if(powerlevel= get_hunger_nutrition() + 100) //can't get power levels unless you're a bit above hunger level.
- if(powerlevel<5)
+ else if(nutrition >= hunger_nutrition + 100) //can't get power levels unless you're a bit above hunger level.
+ if(powerlevel 50)
- attacked_stacks = 50
-
- if(attacked_stacks > 0)
- attacked_stacks--
-
- if(discipline_stacks > 0)
-
- if(discipline_stacks >= 5 && rabid)
- if(SPT_PROB(37, seconds_per_tick))
- rabid = FALSE
-
- if(SPT_PROB(5, seconds_per_tick))
- discipline_stacks--
-
- if(client) //player controlled slimes can decide for themselves
- return
-
- if(!(mobility_flags & MOBILITY_MOVE))
- return
-
- if(buckled)
- return // if it's eating someone already, continue eating!
-
- 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)
-
- if(slime_ai_processing && stunned_until > world.time)
- return
-
- var/hungry = SLIME_HUNGER_NONE // determines if the slime is hungry
-
- 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(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)
-
- 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()
-
- 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(locate(/mob/living/simple_animal/slime) in L.buckled_mobs) // Only one slime can latch on at a time.
- continue
-
- 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(islarva(C) || ismonkey(C))
- set_target(C)
- break
-
- if (Target)
- target_patience = rand(5, 7)
- if (is_adult)
- target_patience += 3
-
- 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(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()
-
-/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)
- #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 = SLIME_MOOD_SMILE
- else if (Target)
- newmood = SLIME_MOOD_MISCHIEVOUS
-
- if (!newmood)
- if (discipline_stacks && SPT_PROB(13, seconds_per_tick))
- newmood = SLIME_MOOD_POUT
- else if (SPT_PROB(0.5, seconds_per_tick))
- newmood = pick(SLIME_MOOD_SAD, ":3", SLIME_MOOD_POUT)
-
- 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
-
- if (newmood != current_mood) // This is so we don't redraw them every time
- 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(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"))
- if (Leader)
- if (Leader == who) // Already following him
- to_say = pick("Yes...", "Lead...", "Follow...")
- else if (Friends[who] > Friends[Leader]) // VIVA
- set_leader(who)
- to_say = "Yes... I follow [who]..."
- else
- to_say = "No... I follow [Leader]..."
- else
- if (Friends[who] >= SLIME_FRIENDSHIP_FOLLOW)
- set_leader(who)
- to_say = "I follow..."
- else // Not friendly enough
- to_say = pick("No...", "I no follow...")
- else if (findtext(phrase, "stop"))
- if (buckled) // We are asked to stop feeding
- if (Friends[who] >= SLIME_FRIENDSHIP_STOPEAT)
- stop_feeding()
- set_target(null)
- if (Friends[who] < SLIME_FRIENDSHIP_STOPEAT_NOANGRY)
- add_friendship(who, -1)
- to_say = "Grrr..." // I'm angry but I do it
- else
- to_say = "Fine..."
- else if (Target) // We are asked to stop chasing
- if (Friends[who] >= SLIME_FRIENDSHIP_STOPCHASE)
- set_target(null)
- if (Friends[who] < SLIME_FRIENDSHIP_STOPCHASE_NOANGRY)
- add_friendship(who, -1)
- to_say = "Grrr..." // I'm angry but I do it
- else
- to_say = "Fine..."
- else if (Leader) // We are asked to stop following
- if (Leader == who)
- to_say = "Yes... I stay..."
- set_leader(null)
- else
- if (Friends[who] > Friends[Leader])
- set_leader(null)
- to_say = "Yes... I stop..."
- else
- to_say = "No... keep follow..."
- else if (findtext(phrase, "stay"))
- if (Leader)
- if (Leader == who)
- holding_still = Friends[who] * 10
- to_say = "Yes... stay..."
- else if (Friends[who] > Friends[Leader])
- holding_still = (Friends[who] - Friends[Leader]) * 10
- to_say = "Yes... stay..."
- else
- to_say = "No... keep follow..."
- else
- if (Friends[who] >= SLIME_FRIENDSHIP_STAY)
- holding_still = Friends[who] * 10
- to_say = "Yes... stay..."
- else
- to_say = "No... won't stay..."
- else if (findtext(phrase, "attack"))
- if (rabid && prob(20))
- set_target(who)
- 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/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[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 [possible_target] ..."
- add_friendship(who, -1) //Don't ask a slime to attack its friend
- break
- else
- to_say = "No... no listen"
-
- speech_buffer = list()
-
- //Speech starts here
- if (to_say)
- say (to_say)
- else if(SPT_PROB(0.5, seconds_per_tick))
- emote(pick("bounce","sway","light","vibrate","jiggle"))
- else
- var/speech_chance = 10
- var/slimes_near = 0
- var/dead_slimes = 0
- var/friends_near = list()
- for (var/mob/living/seen_mob in view(7,src))
- if(isslime(seen_mob) && seen_mob != src)
- ++slimes_near
- if (seen_mob.stat == DEAD)
- ++dead_slimes
- if (seen_mob in Friends)
- speech_chance += 20
- friends_near += seen_mob
- if (nutrition < get_hunger_nutrition())
- speech_chance += 10
- if (nutrition < get_starve_nutrition())
- speech_chance += 10
- if (SPT_PROB(1, seconds_per_tick) && prob(speech_chance))
- var/phrases = list()
- if (Target)
- phrases += "[Target]... look yummy..."
- if (nutrition < get_starve_nutrition())
- phrases += "So... hungry..."
- phrases += "Very... hungry..."
- phrases += "Need... food..."
- phrases += "Must... eat..."
- else if (nutrition < get_hunger_nutrition())
- phrases += "Hungry..."
- phrases += "Where food?"
- phrases += "I want to eat..."
- phrases += "Rawr..."
- phrases += "Blop..."
- phrases += "Blorble..."
- if (rabid || attacked_stacks)
- phrases += "Hrr..."
- phrases += "Nhuu..."
- phrases += "Unn..."
- if (current_mood == ":3")
- phrases += "Purr..."
- if (attacked_stacks)
- phrases += "Grrr..."
- if (bodytemperature < T0C)
- phrases += "Cold..."
- if (bodytemperature < T0C - 30)
- phrases += "So... cold..."
- phrases += "Very... cold..."
- if (bodytemperature < T0C - 50)
- phrases += "..."
- phrases += "C... c..."
- if (buckled)
- phrases += "Nom..."
- phrases += "Yummy..."
- if (powerlevel > 3)
- phrases += "Bzzz..."
- if (powerlevel > 5)
- phrases += "Zap..."
- if (powerlevel > 8)
- phrases += "Zap... Bzz..."
- if (current_mood == "sad")
- phrases += "Bored..."
- if (slimes_near)
- phrases += "Slime friend..."
- if (slimes_near > 1)
- phrases += "Slime friends..."
- if (dead_slimes)
- phrases += "What happened?"
- if (!slimes_near)
- phrases += "Lonely..."
- for (var/friend in friends_near)
- phrases += "[friend]... friend..."
- if (nutrition < get_hunger_nutrition())
- phrases += "[friend]... feed me..."
- if(!stat)
- say (pick(phrases))
-
-/// Can't go above it
-/mob/living/simple_animal/slime/proc/get_max_nutrition()
- if (is_adult)
- return 1200
- else
- return 1000
-
-/// 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
-
-/// Below it we will always eat
-/mob/living/simple_animal/slime/proc/get_hunger_nutrition()
- if (is_adult)
- return 600
- else
- return 500
-
-/// 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
-/// Check for being stopped from feeding and chasing
-/mob/living/simple_animal/slime/proc/will_hunt(hunger = -1)
- if (docile)
- return FALSE
- 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 32c60f63adc..f129482eb24 100644
--- a/code/modules/mob/living/simple_animal/slime/powers.dm
+++ b/code/modules/mob/living/simple_animal/slime/powers.dm
@@ -133,25 +133,27 @@
///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", \
- "This subject does not have life energy", "This subject is empty", \
- "I am not satisified", "I can not feed from this subject", \
- "I do not feel nourished", "This subject is not food")]!")
+ if(!buckled)
+ return
- var/mob/living/victim = buckled
+ if(!living)
+ to_chat(src, "[pick("This subject is incompatible", \
+ "This subject does not have life energy", "This subject is empty", \
+ "I am not satisified", "I can not feed from this subject", \
+ "I do not feel nourished", "This subject is not food")]!")
- if(istype(victim))
- var/bio_protection = 100 - victim.getarmor(null, BIO)
- if(prob(bio_protection))
- victim.apply_status_effect(/datum/status_effect/slimed, slime_type.rgb_code, slime_type.colour == SLIME_TYPE_RAINBOW)
+ var/mob/living/victim = buckled
- if(!silent)
- visible_message(span_warning("[src] lets go of [buckled]!"), \
- span_notice("I stopped feeding."))
- layer = initial(layer)
- buckled.unbuckle_mob(src,force=TRUE)
+ if(istype(victim))
+ var/bio_protection = 100 - victim.getarmor(null, BIO)
+ if(prob(bio_protection))
+ 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]!"), \
+ span_notice("I stopped feeding."))
+ layer = initial(layer)
+ buckled.unbuckle_mob(src,force=TRUE)
/mob/living/simple_animal/slime/verb/Evolve()
set category = "Slime"
@@ -160,19 +162,16 @@
if(stat)
to_chat(src, "I must be conscious to do this...")
return
- if(is_adult)
+ if(life_stage == SLIME_LIFE_STAGE_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
+ set_life_stage(SLIME_LIFE_STAGE_ADULT)
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()
@@ -196,7 +195,7 @@
if(!isopenturf(loc))
balloon_alert(src, "can't reproduce here!")
- if(!is_adult)
+ if(life_stage != SLIME_LIFE_STAGE_ADULT)
balloon_alert(src, "not old enough to reproduce!")
return
diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm
index d9fbfe8dfb3..1d5006bda5e 100644
--- a/code/modules/mob/living/simple_animal/slime/slime.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime.dm
@@ -1,4 +1,8 @@
-#define SLIME_CARES_ABOUT(to_check) (to_check && (to_check == Target || to_check == Leader || (to_check in Friends)))
+#define SLIME_EXTRA_SHOCK_COST 3
+#define SLIME_EXTRA_SHOCK_THRESHOLD 8
+#define SLIME_BASE_SHOCK_PERCENTAGE 10
+#define SLIME_SHOCK_PERCENTAGE_PER_LEVEL 7
+
/mob/living/simple_animal/slime
name = "grey baby slime (123)"
icon = 'icons/mob/simple/slimes.dmi'
@@ -10,6 +14,8 @@
harm_intent_damage = 5
icon_living = "grey baby slime"
icon_dead = "grey baby slime dead"
+ attack_verb_simple = "glomp"
+ attack_verb_continuous = "glomps"
response_help_continuous = "pets"
response_help_simple = "pet"
response_disarm_continuous = "shoos"
@@ -28,6 +34,7 @@
mob_biotypes = MOB_SLIME
melee_damage_lower = 5
melee_damage_upper = 25
+ wound_bonus = -45
verb_say = "blorbles"
verb_ask = "inquisitively blorbles"
@@ -42,17 +49,25 @@
//Physiology
- ///Is the slime an adult slime?
- var/is_adult = TRUE
+ ///What is our current lifestage?
+ var/life_stage = SLIME_LIFE_STAGE_BABY
///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
+ var/powerlevel = SLIME_MIN_POWER
///Controls how long the slime has been overfed, if 10, grows or reproduces
var/amount_grown = 0
+ ///The maximum amount of nutrition a slime can contain
+ var/max_nutrition = 1000
+ /// Above it we grow our amount_grown and our power_level, below it we can eat
+ var/grow_nutrition = 800
+ /// Below this, we feel hungry
+ var/hunger_nutrition = 500
+ /// Below this, we feel starving
+ var/starve_nutrition = 200
///Has a mutator been used on the slime? Only one is allowed
var/mutator_used = FALSE
@@ -106,20 +121,12 @@
///Last phrase said near it and person who said it
var/list/speech_buffer = list()
-/mob/living/simple_animal/slime/Initialize(mapload, new_type=/datum/slime_type/grey, new_is_adult=FALSE)
+/mob/living/simple_animal/slime/Initialize(mapload, new_type=/datum/slime_type/grey, new_life_stage=SLIME_LIFE_STAGE_BABY)
var/datum/action/innate/slime/feed/feeding_action = new
feeding_action.Grant(src)
- is_adult = new_is_adult
+ set_life_stage(new_life_stage)
- if(is_adult)
- var/datum/action/innate/slime/reproduce/reproduce_action = new
- reproduce_action.Grant(src)
- health = 200
- maxHealth = 200
- else
- var/datum/action/innate/slime/evolve/evolve_action = new
- evolve_action.Grant(src)
set_slime_type(new_type)
. = ..()
set_nutrition(700)
@@ -129,6 +136,8 @@
ADD_TRAIT(src, TRAIT_CANT_RIDE, INNATE_TRAIT)
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
+ RegisterSignal(src, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(slime_pre_attack))
+
/mob/living/simple_animal/slime/Destroy()
for (var/A in actions)
var/datum/action/AC = A
@@ -139,34 +148,25 @@
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))
+/mob/living/simple_animal/slime/random/Initialize(mapload, new_colour, new_life_stage)
+ . = ..(mapload, pick(subtypesof(/datum/slime_type)), prob(50) ? SLIME_LIFE_STAGE_ADULT : SLIME_LIFE_STAGE_BABY)
///Friendly docile subtype
/mob/living/simple_animal/slime/pet
docile = TRUE
-/mob/living/simple_animal/slime/proc/set_slime_type(new_type)
- slime_type = new new_type
- update_name()
- 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))
slime_id = rand(1, 1000)
- name = "[slime_type.colour] [is_adult ? "adult" : "baby"] slime ([slime_id])"
+ name = "[slime_type.colour] [life_stage] slime ([slime_id])"
real_name = name
return ..()
-///randomizes the colour of a slime
-/mob/living/simple_animal/slime/proc/random_colour()
- set_slime_type(pick(subtypesof(/datum/slime_type)))
-
/mob/living/simple_animal/slime/regenerate_icons()
cut_overlays()
- var/icon_text = "[slime_type.colour] [is_adult ? "adult" : "baby"] slime"
+ var/icon_text = "[slime_type.colour] [life_stage] slime"
icon_dead = "[icon_text] dead"
if(stat != DEAD)
icon_state = icon_text
@@ -197,39 +197,46 @@
if(mod)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/slime_tempmod, multiplicative_slowdown = mod)
-/mob/living/simple_animal/slime/ObjBump(obj/O)
- if(!client && powerlevel > 0)
- var/probab = 10
- switch(powerlevel)
- if(1 to 2)
- probab = 20
- if(3 to 4)
- probab = 30
- if(5 to 6)
- probab = 40
- if(7 to 8)
- probab = 60
- if(9)
- probab = 70
- if(10)
- probab = 95
- if(prob(probab))
- if(istype(O, /obj/structure/window) || istype(O, /obj/structure/grille))
- if(nutrition <= get_hunger_nutrition() && !is_attack_on_cooldown)
- if (is_adult || prob(5))
- O.attack_slime(src)
- is_attack_on_cooldown = TRUE
- addtimer(VARSET_CALLBACK(src, is_attack_on_cooldown, FALSE), 4.5 SECONDS)
+/mob/living/simple_animal/slime/ObjBump(obj/bumped_object)
+ if(client || powerlevel <= SLIME_MIN_POWER) // slimes with people in control can't accidentally attack
+ return
-/mob/living/simple_animal/slime/Process_Spacemove(movement_dir = 0, continuous_move = FALSE)
- return 2
+ if (life_stage != SLIME_LIFE_STAGE_ADULT && !prob(5)) //its rare for baby slimes to actually damage windows
+ return
+
+ var/accidental_attack_probability = 10
+ switch(powerlevel)
+ if(1 to 2)
+ accidental_attack_probability = 20
+ if(3 to 4)
+ accidental_attack_probability = 30
+ if(5 to 6)
+ accidental_attack_probability = 40
+ if(7 to 8)
+ accidental_attack_probability = 60
+ if(9)
+ accidental_attack_probability = 70
+ if(10)
+ accidental_attack_probability = 95
+ if(!prob(accidental_attack_probability))
+ return
+
+ if(!istype(bumped_object, /obj/structure/window) && !istype(bumped_object, /obj/structure/grille))
+ return
+
+ if(nutrition > hunger_nutrition || is_attack_on_cooldown) //hungry slimes and slimes on cooldown will not attack
+ return
+
+ bumped_object.attack_animal(src)
+ is_attack_on_cooldown = TRUE
+ addtimer(VARSET_CALLBACK(src, is_attack_on_cooldown, FALSE), 4.5 SECONDS)
/mob/living/simple_animal/slime/get_status_tab_items()
. = ..()
if(!docile)
- . += "Nutrition: [nutrition]/[get_max_nutrition()]"
+ . += "Nutrition: [nutrition]/[max_nutrition]"
if(amount_grown >= SLIME_EVOLUTION_THRESHOLD)
- if(is_adult)
+ if(life_stage == SLIME_LIFE_STAGE_ADULT)
. += "You can reproduce!"
else
. += "You can evolve!"
@@ -241,17 +248,6 @@
. += "Power Level: [powerlevel]"
-/mob/living/simple_animal/slime/adjustFireLoss(amount, updating_health = TRUE, forced = FALSE, required_bodytype)
- if(!forced)
- amount = -abs(amount)
- return ..() //Heals them
-
-/mob/living/simple_animal/slime/emp_act(severity)
- . = ..()
- if(. & EMP_PROTECT_SELF)
- return
- powerlevel = 0 // oh no, the power!
-
/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
@@ -268,173 +264,9 @@
/mob/living/simple_animal/slime/attack_ui(slot, params)
return
-/mob/living/simple_animal/slime/attack_slime(mob/living/simple_animal/slime/attacking_slime, list/modifiers)
- if(..()) //successful slime attack
- if(attacking_slime == src)
- return
- if(buckled)
- stop_feeding(silent = TRUE)
- visible_message(span_danger("[attacking_slime] pulls [src] off!"), \
- span_danger("You pull [src] off!"))
- return
- attacked_stacks += 5
- if(nutrition >= 100) //steal some nutrition. negval handled in life()
- adjust_nutrition(-(50 + (40 * attacking_slime.is_adult)))
- attacking_slime.add_nutrition(50 + (40 * attacking_slime.is_adult))
- if(health > 0)
- 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_stacks += 10
-
-/mob/living/simple_animal/slime/attack_paw(mob/living/carbon/human/user, list/modifiers)
- if(..()) //successful monkey bite.
- attacked_stacks += 10
-
-/mob/living/simple_animal/slime/attack_larva(mob/living/carbon/alien/larva/L, list/modifiers)
- if(..()) //successful larva bite.
- attacked_stacks += 10
-
-/mob/living/simple_animal/slime/attack_hulk(mob/living/carbon/human/user)
- . = ..()
- if(!.)
- return
- discipline_slime(user)
-
-/mob/living/simple_animal/slime/attack_hand(mob/living/carbon/human/user, list/modifiers)
- if(buckled)
- user.do_attack_animation(src, ATTACK_EFFECT_DISARM)
- if(buckled == user)
- if(prob(60))
- user.visible_message(span_warning("[user] attempts to wrestle \the [name] off!"), \
- span_danger("You attempt to wrestle \the [name] off!"))
- playsound(loc, 'sound/weapons/punchmiss.ogg', 25, TRUE, -1)
-
- else
- user.visible_message(span_warning("[user] manages to wrestle \the [name] off!"), \
- span_notice("You manage to wrestle \the [name] off!"))
- playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
-
- discipline_slime(user)
-
- else
- if(prob(30))
- buckled.visible_message(span_warning("[user] attempts to wrestle \the [name] off of [buckled]!"), \
- span_warning("[user] attempts to wrestle \the [name] off of you!"))
- playsound(loc, 'sound/weapons/punchmiss.ogg', 25, TRUE, -1)
-
- else
- buckled.visible_message(span_warning("[user] manages to wrestle \the [name] off of [buckled]!"), \
- span_notice("[user] manage to wrestle \the [name] off of you!"))
- playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
-
- discipline_slime(user)
- else
- if(stat == DEAD && surgeries.len)
- 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(..()) //successful attack
- 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_stacks += 10
- discipline_slime(user)
-
-
-/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(attacking_item, /obj/item/stack/sheet/mineral/plasma) && !stat) //Lets 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/sheet = attacking_item
- sheet.use(1)
- return
- 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("[attacking_item] passes right through [src]!"))
- return
- 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(attacking_item.force/2)
- if(prob(10 + force_effect))
- discipline_slime(user)
-
- 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/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(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)
- return
-
- if(Target) // Like cats
- set_target(null)
- ++discipline_stacks
- return
+/mob/living/simple_animal/slime/get_mob_buckling_height(mob/seat)
+ if(..())
+ return 3
/mob/living/simple_animal/slime/examine(mob/user)
. = list("This is [icon2html(src, user)] \a [src]!")
@@ -466,6 +298,70 @@
. += ""
+/mob/living/simple_animal/slime/proc/apply_water()
+ adjustBruteLoss(rand(15,20))
+ if(client)
+ return
+
+ if(Target) // Like cats
+ set_target(null)
+ ++discipline_stacks
+ return
+
+///Changes the slime's current life state
+/mob/living/simple_animal/slime/proc/set_life_stage(new_life_stage = SLIME_LIFE_STAGE_BABY)
+ life_stage = new_life_stage
+
+ switch(life_stage)
+ if(SLIME_LIFE_STAGE_BABY)
+ for(var/datum/action/innate/slime/reproduce/reproduce_action in actions)
+ reproduce_action.Remove(src)
+
+ GRANT_ACTION(/datum/action/innate/slime/evolve)
+
+ health = initial(health)
+ maxHealth = initial(maxHealth)
+
+ obj_damage = initial(obj_damage)
+ melee_damage_lower = initial(melee_damage_lower)
+ melee_damage_upper = initial(melee_damage_upper)
+ wound_bonus = initial(wound_bonus)
+
+ max_nutrition = initial(max_nutrition)
+ grow_nutrition = initial(grow_nutrition)
+ hunger_nutrition = initial(hunger_nutrition)
+ starve_nutrition = initial(starve_nutrition)
+
+ if(SLIME_LIFE_STAGE_ADULT)
+
+ for(var/datum/action/innate/slime/evolve/evolve_action in actions)
+ evolve_action.Remove(src)
+
+ GRANT_ACTION(/datum/action/innate/slime/reproduce)
+
+ health = 200
+ maxHealth = 200
+
+ obj_damage = 15
+ melee_damage_lower += 10
+ melee_damage_upper += 10
+ wound_bonus = -90
+
+ max_nutrition += 200
+ grow_nutrition += 200
+ hunger_nutrition += 100
+ starve_nutrition += 100
+
+///Sets the slime's type, name and its icons
+/mob/living/simple_animal/slime/proc/set_slime_type(new_type)
+ slime_type = new new_type
+ update_name()
+ regenerate_icons()
+
+///randomizes the colour of a slime
+/mob/living/simple_animal/slime/proc/random_colour()
+ set_slime_type(pick(subtypesof(/datum/slime_type)))
+
///Makes a slime not attack people for a while
/mob/living/simple_animal/slime/proc/discipline_slime(mob/user)
if(stat)
@@ -474,7 +370,7 @@
if(prob(80) && !client)
discipline_stacks++
- if(!is_adult && discipline_stacks == 1) //if the slime is a baby and has not been overly disciplined, it will give up its grudge
+ if(life_stage == SLIME_LIFE_STAGE_BABY && 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)
@@ -494,66 +390,83 @@
if(user)
step_away(src,user,15)
-/mob/living/simple_animal/slime/get_mob_buckling_height(mob/seat)
- if(..())
- return 3
+///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/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(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)
-///Sets the slime's current attack target
-/mob/living/simple_animal/slime/proc/set_target(new_target)
- var/old_target = Target
- Target = new_target
- if(old_target && !SLIME_CARES_ABOUT(old_target))
- UnregisterSignal(old_target, COMSIG_QDELETING)
- 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
- if(old_leader && !SLIME_CARES_ABOUT(old_leader))
- UnregisterSignal(old_leader, COMSIG_QDELETING)
- 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
- Friends[new_friend] += amount
- 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)
+///Handles slime attacking restrictions, and any extra effects that would trigger
+/mob/living/simple_animal/slime/proc/slime_pre_attack(mob/living/simple_animal/slime/our_slime, atom/target, proximity, modifiers)
SIGNAL_HANDLER
- if(source == Target)
- set_target(null)
- if(source == Leader)
- set_leader(null)
- remove_friend(source)
+ if(isAI(target)) //The aI is not tasty!
+ target.balloon_alert(our_slime, "not tasty!")
+ return COMPONENT_CANCEL_ATTACK_CHAIN
-#undef SLIME_CARES_ABOUT
+ if(our_slime.buckled == target) //If you try to attack the creature you are latched on, you instead cancel feeding
+ our_slime.stop_feeding()
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+
+ if(iscyborg(target))
+ var/mob/living/silicon/robot/borg_target = target
+ borg_target.flash_act()
+ do_sparks(5, TRUE, borg_target)
+ var/stunprob = our_slime.powerlevel * SLIME_SHOCK_PERCENTAGE_PER_LEVEL + SLIME_BASE_SHOCK_PERCENTAGE
+ if(prob(stunprob) && our_slime.powerlevel >= SLIME_EXTRA_SHOCK_COST)
+ our_slime.powerlevel = clamp(our_slime.powerlevel - SLIME_EXTRA_SHOCK_COST, SLIME_MIN_POWER, SLIME_MAX_POWER)
+ borg_target.apply_damage(our_slime.powerlevel * rand(6, 10), BRUTE, spread_damage = TRUE, wound_bonus = CANT_WOUND)
+ borg_target.visible_message(span_danger("The [our_slime.name] shocks [borg_target]!"), span_userdanger("The [our_slime.name] shocks you!"))
+ else
+ borg_target.visible_message(span_danger("The [our_slime.name] fails to hurt [borg_target]!"), span_userdanger("The [our_slime.name] failed to hurt you!"))
+
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+
+ if(iscarbon(target) && our_slime.powerlevel > SLIME_MIN_POWER)
+ var/mob/living/carbon/carbon_target = target
+ var/stunprob = our_slime.powerlevel * SLIME_SHOCK_PERCENTAGE_PER_LEVEL + SLIME_BASE_SHOCK_PERCENTAGE // 17 at level 1, 80 at level 10
+ if(!prob(stunprob))
+ return NONE // normal attack
+
+ carbon_target.visible_message(span_danger("The [our_slime.name] shocks [carbon_target]!"), span_userdanger("The [our_slime.name] shocks you!"))
+
+ do_sparks(5, TRUE, carbon_target)
+ var/power = our_slime.powerlevel + rand(0,3)
+ carbon_target.Paralyze(power * 2 SECONDS)
+ carbon_target.set_stutter_if_lower(power * 2 SECONDS)
+ if (prob(stunprob) && our_slime.powerlevel >= SLIME_EXTRA_SHOCK_COST)
+ our_slime.powerlevel = clamp(our_slime.powerlevel - SLIME_EXTRA_SHOCK_COST, SLIME_MIN_POWER, SLIME_MAX_POWER)
+ carbon_target.apply_damage(our_slime.powerlevel * rand(6, 10), BURN, spread_damage = TRUE, wound_bonus = CANT_WOUND)
+
+ if(isslime(target))
+ if(target == our_slime)
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+ var/mob/living/simple_animal/slime/target_slime = target
+ if(target_slime.buckled)
+ target_slime.stop_feeding(silent = TRUE)
+ visible_message(span_danger("[our_slime] pulls [target_slime] off!"), \
+ span_danger("You pull [target_slime] off!"))
+ return NONE // normal attack
+ target_slime.attacked_stacks += 5
+ var/is_adult_slime = our_slime.life_stage == SLIME_LIFE_STAGE_ADULT
+ if(target_slime.nutrition >= 100) //steal some nutrition. negval handled in life()
+ var/stolen_nutrition = is_adult_slime ? 90 : 50
+ target_slime.adjust_nutrition(-stolen_nutrition)
+ our_slime.add_nutrition(stolen_nutrition)
+ if(target_slime.health > 0)
+ our_slime.adjustBruteLoss(is_adult_slime ? -20 : -10)
+
+#undef SLIME_EXTRA_SHOCK_COST
+#undef SLIME_EXTRA_SHOCK_THRESHOLD
+#undef SLIME_BASE_SHOCK_PERCENTAGE
+#undef SLIME_SHOCK_PERCENTAGE_PER_LEVEL
diff --git a/code/modules/research/xenobiology/crossbreeding/burning.dm b/code/modules/research/xenobiology/crossbreeding/burning.dm
index 8d849c36569..23daa833838 100644
--- a/code/modules/research/xenobiology/crossbreeding/burning.dm
+++ b/code/modules/research/xenobiology/crossbreeding/burning.dm
@@ -36,7 +36,7 @@ Burning extracts:
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!
+ new_slime.set_nutrition(new_slime.hunger_nutrition) //Tonight, we fight!
..()
/obj/item/slimecross/burning/orange
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index 70be6275763..8ebfc5719f6 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -73,7 +73,7 @@
if(target_slime.stat)
to_chat(user, span_warning("The slime is dead!"))
return
- if(!target_slime.is_adult)
+ if(target_slime.life_stage != SLIME_LIFE_STAGE_ADULT)
to_chat(user, span_warning("The slime must be an adult to cross its core!"))
return
if(target_slime.crossbreed_modification && target_slime.crossbreed_modification != crossbreed_modification)
@@ -804,22 +804,22 @@
icon = 'icons/obj/medical/chemical.dmi'
icon_state = "potred"
-/obj/item/slimepotion/slime/steroid/attack(mob/living/simple_animal/slime/M, mob/user)
- if(!isslime(M))//If target is not a slime.
+/obj/item/slimepotion/slime/steroid/attack(mob/living/simple_animal/slime/target, mob/user)
+ if(!isslime(target))//If target is not a slime.
to_chat(user, span_warning("The steroid only works on baby slimes!"))
return ..()
- if(M.is_adult) //Can't steroidify adults
+ if(target.life_stage == SLIME_LIFE_STAGE_ADULT) //Can't steroidify adults
to_chat(user, span_warning("Only baby slimes can use the steroid!"))
return
- if(M.stat)
+ if(target.stat)
to_chat(user, span_warning("The slime is dead!"))
return
- if(M.cores >= 5)
+ if(target.cores >= 5)
to_chat(user, span_warning("The slime already has the maximum amount of extract!"))
return
to_chat(user, span_notice("You feed the slime the steroid. It will now produce one more extract."))
- M.cores++
+ target.cores++
qdel(src)
/obj/item/slimepotion/enhancer
diff --git a/tgstation.dme b/tgstation.dme
index c50c3389ceb..7658cfb8564 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -4951,7 +4951,9 @@
#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\elites\pandora.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\goose.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\retaliate.dm"
+#include "code\modules\mob\living\simple_animal\slime\ai.dm"
#include "code\modules\mob\living\simple_animal\slime\death.dm"
+#include "code\modules\mob\living\simple_animal\slime\defense.dm"
#include "code\modules\mob\living\simple_animal\slime\emote.dm"
#include "code\modules\mob\living\simple_animal\slime\life.dm"
#include "code\modules\mob\living\simple_animal\slime\powers.dm"