diff --git a/code/_macros.dm b/code/_macros.dm
index d134a282a4..cd28ee612d 100644
--- a/code/_macros.dm
+++ b/code/_macros.dm
@@ -42,7 +42,7 @@
#define isvoice(A) istype(A, /mob/living/voice)
-#define isslime(A) istype(A, /mob/living/simple_animal/slime)
+#define isslime(A) istype(A, /mob/living/simple_mob/slime)
#define isbot(A) istype(A, /mob/living/bot)
diff --git a/code/modules/admin/verbs/lightning_strike.dm b/code/modules/admin/verbs/lightning_strike.dm
index 16cb6b06dd..88fab6723e 100644
--- a/code/modules/admin/verbs/lightning_strike.dm
+++ b/code/modules/admin/verbs/lightning_strike.dm
@@ -87,7 +87,7 @@
// The actual damage/electrocution is handled by tesla_zap().
L.Paralyse(5)
L.stuttering += 20
- L.make_jittery(20)
+ L.make_jittery(150)
L.emp_act(1)
to_chat(L, span("critical", "You've been struck by lightning!"))
@@ -102,6 +102,8 @@
SA.ash()
continue // No point deafening something that wont exist.
+ if(istype(L, /mob/living/simple
+
// Deafen them.
if(L.get_ear_protection() < 2)
L.AdjustSleeping(-100)
diff --git a/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai.dm b/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai.dm
new file mode 100644
index 0000000000..e774cdfb52
--- /dev/null
+++ b/code/modules/ai/aI_holder_subtypes/slime_xenobio_ai.dm
@@ -0,0 +1,179 @@
+// Specialized AI for slime simplemobs.
+// Unlike the parent AI code, this will probably break a lot of things if you put it on something that isn't /mob/living/simple_animal/slime/xenobio
+
+/datum/ai_holder/simple_mob/xenobio_slime
+ hostile = TRUE
+ cooperative = TRUE
+ firing_lanes = TRUE
+ var/rabid = FALSE // Will attack regardless of discipline.
+ var/discipline = 0 // Beating slimes makes them less likely to lash out. In theory.
+ var/resentment = 0 // 'Unjustified' beatings make this go up, and makes it more likely for abused slimes to go rabid.
+ var/obedience = 0 // Conversely, 'justified' beatings make this go up, and makes discipline decay slowly, potentially making it not decay at all.
+
+ var/always_stun = FALSE // If true, the slime will elect to attempt to permastun the target.
+
+/datum/ai_holder/simple_mob/xenobio_slime/sapphire
+ always_stun = TRUE // They know that stuns are godly.
+
+
+/datum/ai_holder/simple_mob/xenobio_slime/New()
+ ..()
+ ASSERT(istype(holder, /mob/living/simple_mob/slime/xenobio))
+
+// Checks if disciplining the slime would be 'justified' right now.
+/datum/ai_holder/simple_mob/xenobio_slime/proc/is_justified_to_discipline()
+ if(rabid)
+ return TRUE
+ if(target)
+ if(ishuman(target))
+ var/mob/living/carbon/human/H = target
+ if(istype(H.species, /datum/species/monkey))
+ return FALSE // Attacking monkeys is okay.
+ return TRUE // Otherwise attacking other things is bad.
+ return FALSE // Not attacking anything.
+
+/datum/ai_holder/simple_mob/xenobio_slime/proc/can_command(mob/living/commander)
+ if(rabid)
+ return FALSE
+ if(!hostile)
+ return SLIME_COMMAND_OBEY
+// if(commander in friends)
+// return SLIME_COMMAND_FRIEND
+ if(holder.IIsAlly(commander))
+ return SLIME_COMMAND_FACTION
+ if(discipline > resentment && obedience >= 5)
+ return SLIME_COMMAND_OBEY
+ return FALSE
+
+/datum/ai_holder/simple_mob/xenobio_slime/proc/adjust_discipline(amount, silent)
+ var/mob/living/simple_mob/slime/xenobio/my_slime = holder
+ if(amount > 0)
+ if(rabid)
+ return
+ var/justified = is_justified_to_discipline()
+ lost_target() // Stop attacking.
+
+ if(justified)
+ obedience++
+ if(!silent)
+ holder.say(pick("Fine...", "Okay...", "Sorry...", "I yield...", "Mercy..."))
+ else
+ if(prob(resentment * 20))
+ enrage()
+ holder.say(pick("Evil...", "Kill...", "Tyrant..."))
+ else
+ if(!silent)
+ holder.say(pick("Why...?", "I don't understand...?", "Cruel...", "Stop...", "Nooo..."))
+ resentment++ // Done after check so first time will never enrage.
+
+ discipline = between(0, discipline + amount, 10)
+ my_slime.update_mood()
+
+/datum/ai_holder/simple_mob/xenobio_slime/handle_special_strategical()
+ discipline_decay()
+
+// Handles decay of discipline.
+/datum/ai_holder/simple_mob/xenobio_slime/proc/discipline_decay()
+ if(discipline > 0)
+ if(!prob(75 + (obedience * 5)))
+ adjust_discipline(-1)
+
+/datum/ai_holder/simple_mob/xenobio_slime/handle_special_tactic()
+ evolve_and_reproduce()
+
+// Hit the correct verbs to keep the slime species going.
+/datum/ai_holder/simple_mob/xenobio_slime/proc/evolve_and_reproduce()
+ var/mob/living/simple_mob/slime/xenobio/my_slime = holder
+ if(my_slime.amount_grown >= 10)
+ // Press the correct verb when we can.
+ if(my_slime.is_adult)
+ my_slime.reproduce() // Splits into four new baby slimes.
+ else
+ my_slime.evolve() // Turns our holder into an adult slime.
+
+
+// Called when pushed too far (or a red slime core was used).
+/datum/ai_holder/simple_mob/xenobio_slime/proc/enrage()
+ var/mob/living/simple_mob/slime/xenobio/my_slime = holder
+ if(my_slime.harmless)
+ return
+ rabid = TRUE
+ my_slime.update_mood()
+ my_slime.visible_message(span("danger", "\The [src] enrages!"))
+
+// Called when using a pacification agent (or it's Kendrick being initalized).
+/datum/ai_holder/simple_mob/xenobio_slime/proc/pacify()
+ lost_target() // So it stops trying to kill them.
+ rabid = FALSE
+ hostile = FALSE
+ retaliate = FALSE
+ cooperative = FALSE
+
+// The holder's attack changes based on intent. This lets the AI choose what effect is desired.
+/datum/ai_holder/simple_mob/xenobio_slime/pre_melee_attack(atom/A)
+ if(istype(A, /mob/living))
+ var/mob/living/L = A
+ var/mob/living/simple_mob/slime/xenobio/my_slime = holder
+
+ if( (!L.lying && prob(30 + (my_slime.power_charge * 7) ) || (!L.lying && always_stun) ))
+ my_slime.a_intent = I_DISARM // Stun them first.
+ else if(my_slime.can_consume(L) && L.lying)
+ my_slime.a_intent = I_GRAB // Then eat them.
+ else
+ my_slime.a_intent = I_HURT // Otherwise robust them.
+
+/datum/ai_holder/simple_mob/xenobio_slime/closest_distance(atom/movable/AM)
+ if(istype(AM, /mob/living))
+ var/mob/living/L = AM
+ if(ishuman(L))
+ var/mob/living/carbon/human/H = L
+ if(istype(H.species, /datum/species/monkey))
+ return 1 // Otherwise ranged slimes will eat a lot less often.
+ if(L.stat >= UNCONSCIOUS)
+ return 1 // Melee (eat) the target if dead/dying, don't shoot it.
+ return ..()
+
+/datum/ai_holder/simple_mob/xenobio_slime/can_attack(atom/movable/AM)
+ . = ..()
+ if(.) // Do some additional checks because we have Special Code(tm).
+ if(ishuman(AM))
+ var/mob/living/carbon/human/H = AM
+ if(istype(H.species, /datum/species/monkey)) // istype() is so they'll eat the alien monkeys too.
+ return TRUE // Monkeys are always food (sorry Pun Pun).
+ else if(H.species && H.species.name == SPECIES_PROMETHEAN)
+ return FALSE // Prometheans are always our friends.
+ if(discipline && !rabid)
+ return FALSE // We're a good slime.
+
+/*
+
+/mob/living/simple_animal/slime/special_target_check(mob/living/L)
+ if(L.faction == faction && !attack_same && !istype(L, /mob/living/simple_animal/slime))
+ if(ishuman(L))
+ var/mob/living/carbon/human/H = L
+ if(istype(H.species, /datum/species/monkey)) // istype() is so they'll eat the alien monkeys too.
+ return TRUE // Monkeys are always food.
+ else
+ return FALSE
+ if(L in friends)
+ return FALSE
+
+ if(istype(L, /mob/living/simple_animal/slime))
+ var/mob/living/simple_animal/slime/buddy = L
+ if(buddy.slime_color == src.slime_color || discipline || unity || buddy.unity)
+ return FALSE // Don't hurt same colored slimes.
+
+ if(ishuman(L))
+ var/mob/living/carbon/human/H = L
+ if(H.species && H.species.name == "Promethean")
+ return FALSE // Prometheans are always our friends.
+ else if(istype(H.species, /datum/species/monkey)) // istype() is so they'll eat the alien monkeys too.
+ return TRUE // Monkeys are always food.
+ if(discipline && !rabid)
+ return FALSE // We're a good slime. For now at least
+
+ if(issilicon(L) || isbot(L) )
+ if(discipline && !rabid)
+ return FALSE // We're a good slime. For now at least.
+ return ..() // Other colors and nonslimes are jerks however.
+*/
\ No newline at end of file
diff --git a/code/modules/ai/ai_holder.dm b/code/modules/ai/ai_holder.dm
index 47ff4110a1..ad4322dffd 100644
--- a/code/modules/ai/ai_holder.dm
+++ b/code/modules/ai/ai_holder.dm
@@ -64,9 +64,18 @@
/datum/ai_holder/proc/go_wake()
if(stance != STANCE_SLEEP)
return
+ if(!should_wake())
+ return
set_stance(STANCE_IDLE)
SSai.processing += src
+/datum/ai_holder/proc/should_wake()
+ if(holder.client && !autopilot)
+ return FALSE
+ if(holder.stat >= DEAD)
+ return FALSE
+ return TRUE
+
// Resets a lot of 'memory' vars.
/datum/ai_holder/proc/forget_everything()
// Some of these might be redundant, but hopefully this prevents future bugs if that changes.
@@ -79,14 +88,21 @@
/datum/ai_holder/proc/handle_tactics()
if(busy)
return
+ handle_special_tactic()
handle_stance_tactical()
// 'Strategical' processes that are more expensive on the CPU and so don't get run as often as the above proc, such as A* pathfinding or robust targeting.
/datum/ai_holder/proc/handle_strategicals()
if(busy)
return
+ handle_special_strategical()
handle_stance_strategical()
+// Override these for special things without polluting the main loop.
+/datum/ai_holder/proc/handle_special_tactic()
+
+/datum/ai_holder/proc/handle_special_strategical()
+
/*
//AI Actions
if(!ai_inactive)
@@ -329,6 +345,10 @@
return null
return ai_holder.stance
+// Similar to above but only returns 1 or 0.
+/mob/living/proc/has_AI()
+ return get_AI_stance() ? TRUE : FALSE
+
// 'Taunts' the AI into attacking the taunter.
/mob/living/proc/taunt(atom/movable/taunter, force_target_switch = FALSE)
if(ai_holder)
diff --git a/code/modules/ai/ai_holder_combat.dm b/code/modules/ai/ai_holder_combat.dm
index 8f96612282..0dadf79150 100644
--- a/code/modules/ai/ai_holder_combat.dm
+++ b/code/modules/ai/ai_holder_combat.dm
@@ -89,12 +89,14 @@
// We're not entirely sure how holder will do melee attacks since any /mob/living could be holder, but we don't have to care because Interfaces.
/datum/ai_holder/proc/melee_attack(atom/A)
+ pre_melee_attack(A)
. = holder.IAttack(A)
if(.)
post_melee_attack(A)
// Ditto.
/datum/ai_holder/proc/ranged_attack(atom/A)
+ pre_ranged_attack(A)
. = holder.IRangedAttack(A)
if(.)
post_ranged_attack(A)
@@ -102,7 +104,6 @@
// Most mobs probably won't have this defined but we don't care.
/datum/ai_holder/proc/special_attack(atom/movable/AM)
. = holder.ISpecialAttack(AM)
- world << "special_attack result is [.]"
if(.)
post_special_attack(AM)
@@ -111,6 +112,12 @@
// Note that this is called BEFORE the attack.
/datum/ai_holder/proc/on_engagement(atom/A)
+// Called before a ranged attack is attempted.
+/datum/ai_holder/proc/pre_ranged_attack(atom/A)
+
+// Called before a melee attack is attempted.
+/datum/ai_holder/proc/pre_melee_attack(atom/A)
+
// Called after a successful (IE not on cooldown) ranged attack.
// Note that this is not whether the projectile actually hit, just that one was launched.
/datum/ai_holder/proc/post_ranged_attack(atom/A)
diff --git a/code/modules/ai/ai_holder_cooperation.dm b/code/modules/ai/ai_holder_cooperation.dm
index 638e6112d7..67de139349 100644
--- a/code/modules/ai/ai_holder_cooperation.dm
+++ b/code/modules/ai/ai_holder_cooperation.dm
@@ -56,6 +56,8 @@
for(var/mob/living/L in faction_friends)
if(L == holder) // Lets not call ourselves.
continue
+ if(holder.z != L.z) // On seperate z-level.
+ continue
if(get_dist(L, holder) > call_distance) // Too far to 'hear' the call for help.
continue
diff --git a/code/modules/ai/ai_holder_targeting.dm b/code/modules/ai/ai_holder_targeting.dm
index 0fe8702300..10ab1eda71 100644
--- a/code/modules/ai/ai_holder_targeting.dm
+++ b/code/modules/ai/ai_holder_targeting.dm
@@ -93,6 +93,9 @@
if(!can_see_target(the_target))
return FALSE
+ if(istype(the_target, /mob/zshadow))
+ return FALSE // no
+
if(isliving(the_target))
var/mob/living/L = the_target
if(L.stat == DEAD)
diff --git a/code/modules/ai/interfaces.dm b/code/modules/ai/interfaces.dm
index 59079c8a04..979bb0015f 100644
--- a/code/modules/ai/interfaces.dm
+++ b/code/modules/ai/interfaces.dm
@@ -19,7 +19,7 @@
return FALSE
return shoot_target(A)
-// Test if the AI is allowed to use to attempt a ranged attack.
+// Test if the AI is allowed to attempt a ranged attack.
/mob/living/proc/ICheckRangedAttack(atom/A)
return FALSE
diff --git a/code/modules/mob/_modifiers/modifiers_misc.dm b/code/modules/mob/_modifiers/modifiers_misc.dm
index 5cac1ad5cf..240c990fe8 100644
--- a/code/modules/mob/_modifiers/modifiers_misc.dm
+++ b/code/modules/mob/_modifiers/modifiers_misc.dm
@@ -199,3 +199,18 @@ the artifact triggers the rage.
/datum/modifier/fire/tick()
holder.adjustFireLoss(damage_per_tick)
+
+// Applied when subjected to a cold attack.
+// Reduces mobility, attack speed.
+/datum/modifier/chilled
+ name = "chilled"
+ desc = "You feel yourself freezing up. Its hard to move."
+ mob_overlay_state = "chilled"
+
+ on_created_text = "You feel like you're going to freeze! It's hard to move."
+ on_expired_text = "You feel somewhat warmer and more mobile now."
+
+ slowdown = 2
+ evasion = -40
+ attack_speed_percent = 1.4
+ disable_duration_percent = 1.2
\ No newline at end of file
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 7ff23130d1..5a48b26fdb 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -634,6 +634,8 @@ default behaviour is:
BITSET(hud_updateflag, LIFE_HUD)
ExtinguishMob()
fire_stacks = 0
+ if(ai_holder) // AI gets told to sleep when killed. Since they're not dead anymore, wake it up.
+ ai_holder.go_wake()
/mob/living/proc/rejuvenate()
if(reagents)
diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm
index 843990c7f6..2b5d44ded9 100644
--- a/code/modules/mob/living/simple_animal/slime/slime.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime.dm
@@ -17,8 +17,8 @@
maxHealth = 150
var/maxHealth_adult = 200
- melee_damage_lower = 5
- melee_damage_upper = 25
+ melee_damage_lower = 10
+ melee_damage_upper = 15
melee_miss_chance = 0
gender = NEUTER
diff --git a/code/modules/mob/living/simple_mob/combat.dm b/code/modules/mob/living/simple_mob/combat.dm
index 389c920be5..2a8aaaed00 100644
--- a/code/modules/mob/living/simple_mob/combat.dm
+++ b/code/modules/mob/living/simple_mob/combat.dm
@@ -45,6 +45,7 @@
if(prob(melee_miss_chance))
add_attack_logs(src, L, "Animal-attacked (miss)", admin_notify = FALSE)
do_attack_animation(src)
+ playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1)
return FALSE // We missed.
if(ishuman(L))
@@ -52,13 +53,18 @@
if(H.check_shields(damage = damage_to_do, damage_source = src, attacker = src, def_zone = null, attack_text = "the attack"))
return FALSE // We were blocked.
- if(A.attack_generic(src, damage_to_do, pick(attacktext)))
+ if(apply_attack(A, damage_to_do))
apply_melee_effects(A)
if(attack_sound)
playsound(src, attack_sound, 75, 1)
return TRUE
+// Generally used to do the regular attack.
+// Override for doing special stuff with the direct result of the attack.
+/mob/living/simple_mob/proc/apply_attack(atom/A, damage_to_do)
+ return A.attack_generic(src, damage_to_do, pick(attacktext))
+
// Override for special effects after a successful attack, like injecting poison or stunning the target.
/mob/living/simple_mob/proc/apply_melee_effects(atom/A)
return
@@ -172,7 +178,7 @@
var/true_attack_delay = attack_delay
for(var/datum/modifier/M in modifiers)
if(!isnull(M.attack_speed_percent))
- attack_delay *= M.attack_speed_percent
+ true_attack_delay *= M.attack_speed_percent
setClickCooldown(true_attack_delay) // Insurance against a really long attack being longer than default click delay.
diff --git a/code/modules/mob/living/simple_mob/defense.dm b/code/modules/mob/living/simple_mob/defense.dm
index fd2585c660..32a2869036 100644
--- a/code/modules/mob/living/simple_mob/defense.dm
+++ b/code/modules/mob/living/simple_mob/defense.dm
@@ -89,8 +89,6 @@
to_chat(user,"This weapon is ineffective, it does no damage.")
return 2 //???
-// react_to_attack(user)
-
. = ..()
diff --git a/code/modules/mob/living/simple_mob/life.dm b/code/modules/mob/living/simple_mob/life.dm
index 2b3fbf50ed..bab2aa4129 100644
--- a/code/modules/mob/living/simple_mob/life.dm
+++ b/code/modules/mob/living/simple_mob/life.dm
@@ -19,7 +19,7 @@
//Should we be dead?
/mob/living/simple_mob/updatehealth()
- health = getMaxHealth() - getToxLoss() - getFireLoss() - getBruteLoss()
+ health = getMaxHealth() - getFireLoss() - getBruteLoss() - getToxLoss() - getOxyLoss() - getCloneLoss()
//Alive, becoming dead
if((stat < DEAD) && (health <= 0))
@@ -116,12 +116,12 @@
//Atmos effect
if(bodytemperature < minbodytemp)
fire_alert = 2
- adjustBruteLoss(cold_damage_per_tick)
+ adjustFireLoss(cold_damage_per_tick)
if(fire)
fire.icon_state = "fire1"
else if(bodytemperature > maxbodytemp)
fire_alert = 1
- adjustBruteLoss(heat_damage_per_tick)
+ adjustFireLoss(heat_damage_per_tick)
if(fire)
fire.icon_state = "fire2"
else
@@ -130,12 +130,13 @@
fire.icon_state = "fire0"
if(atmos_unsuitable)
- adjustBruteLoss(unsuitable_atoms_damage)
+ adjustOxyLoss(unsuitable_atoms_damage)
if(oxygen)
oxygen.icon_state = "oxy1"
else if(oxygen)
if(oxygen)
oxygen.icon_state = "oxy0"
+ adjustOxyLoss(-unsuitable_atoms_damage)
/mob/living/simple_mob/proc/handle_supernatural()
diff --git a/code/modules/mob/living/simple_mob/on_click.dm b/code/modules/mob/living/simple_mob/on_click.dm
index 95c1ea876e..4b824c9580 100644
--- a/code/modules/mob/living/simple_mob/on_click.dm
+++ b/code/modules/mob/living/simple_mob/on_click.dm
@@ -14,14 +14,14 @@
switch(a_intent)
if(I_HELP)
if(isliving(A))
- custom_emote(1,"[pick(friendly)] [A]!")
+ custom_emote(1,"[pick(friendly)] \the [A]!")
if(I_HURT)
if(can_special_attack(A) && special_attack_target(A))
return
else if(melee_damage_upper == 0 && istype(A,/mob/living))
- custom_emote(1,"[pick(friendly)] [A]!")
+ custom_emote(1,"[pick(friendly)] \the [A]!")
else
attack_target(A)
@@ -29,10 +29,14 @@
if(I_GRAB)
if(has_hands)
A.attack_hand(src)
+ else
+ attack_target(A)
if(I_DISARM)
if(has_hands)
A.attack_hand(src)
+ else
+ attack_target(A)
/mob/living/simple_mob/RangedAttack(var/atom/A)
// setClickCooldown(get_attack_speed())
diff --git a/code/modules/mob/living/simple_mob/simple_mob.dm b/code/modules/mob/living/simple_mob/simple_mob.dm
index 475f5054c5..a075bf2625 100644
--- a/code/modules/mob/living/simple_mob/simple_mob.dm
+++ b/code/modules/mob/living/simple_mob/simple_mob.dm
@@ -143,7 +143,7 @@
/mob/living/simple_mob/initialize()
verbs -= /mob/verb/observe
- maxHealth = health
+ health = maxHealth
for(var/L in has_langs)
languages |= all_languages[L]
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm
index 62101b437f..3fb663aa11 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/carp.dm
@@ -13,7 +13,7 @@
maxHealth = 25
health = 25
movement_cooldown = 0 // Carp go fast
- hovering = TRUE.
+ hovering = TRUE
response_help = "pets the"
response_disarm = "gently pushes aside the"
diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm
new file mode 100644
index 0000000000..65be438ad7
--- /dev/null
+++ b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm
@@ -0,0 +1,13 @@
+// These slimes lack certain xenobio features but get more combat-oriented goodies. Generally these are more oriented towards Explorers than Xenobiologists.
+
+/mob/living/simple_mob/slime/feral
+ name = "feral slime"
+ desc = "The result of slimes escaping containment from some xenobiology lab."
+ icon_scale = 2 // Twice as big as the xenobio variant.
+ cores = 6 // Xenobio will love getting their hands on these.
+ pixel_y = -10 // Since the base sprite isn't centered properly, the pixel auto-adjustment needs some help.
+ description_info = "Note that processing this large slime will give six cores."
+
+// Slimebatoning/xenotasing it just makes it mad at you (which can be good if you're heavily armored and your friends aren't).
+/mob/living/simple_mob/slime/feral/slimebatoned(mob/living/user, amount)
+ taunt(user, TRUE)
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm b/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm
new file mode 100644
index 0000000000..71ff3416aa
--- /dev/null
+++ b/code/modules/mob/living/simple_mob/subtypes/slime/slime.dm
@@ -0,0 +1,207 @@
+// The top-level slime defines. Xenobio slimes and feral slimes will inherit from this.
+
+/mob/living/simple_mob/slime
+ name = "slime"
+ desc = "It's a slime."
+ tt_desc = "A Macrolimbus vulgaris"
+ icon = 'icons/mob/slime2.dmi'
+ icon_state = "slime baby"
+ icon_living = "slime baby"
+ icon_dead = "slime baby dead"
+ var/shiny = FALSE // If true, will add a 'shiny' overlay.
+ var/icon_state_override = null // Used for special slime appearances like the rainbow slime.
+ color = "#CACACA"
+ glow_range = 3
+ glow_intensity = 2
+ gender = NEUTER
+
+ faction = "slime" // Note that slimes are hostile to other slimes of different color regardless of faction (unless Unified).
+ maxHealth = 150
+ movement_cooldown = 0
+ pass_flags = PASSTABLE
+ makes_dirt = FALSE // Goop
+ mob_class = MOB_CLASS_SLIME
+
+ response_help = "pets"
+
+ // Atmos stuff.
+ minbodytemp = T0C-30
+ heat_damage_per_tick = 0
+ cold_damage_per_tick = 40
+
+ min_oxy = 0
+ max_oxy = 0
+ min_tox = 0
+ max_tox = 0
+ min_co2 = 0
+ max_co2 = 0
+ min_n2 = 0
+ max_n2 = 0
+ unsuitable_atoms_damage = 0
+ shock_resist = 1 // Slimes are immune to electricity, and it actually charges them.
+ taser_kill = FALSE
+
+ melee_damage_lower = 10
+ melee_damage_upper = 15
+ base_attack_cooldown = 10 // One attack a second.
+ attack_sound = 'sound/weapons/bite.ogg'
+ attacktext = list("glomped")
+ speak_emote = list("chirps")
+ friendly = list("pokes")
+
+ ai_holder_type = /datum/ai_holder/simple_mob/melee
+ say_list_type = /datum/say_list/slime
+
+ var/cores = 1 // How many cores you get when placed in a Processor.
+ var/obj/item/clothing/head/hat = null // The hat the slime may be wearing.
+ var/slime_color = "grey" // Used for updating the name and for slime color-ism.
+ var/unity = FALSE // If true, slimes will consider other colors as their own. Other slimes will see this slime as the same color as well.
+ var/coretype = /obj/item/slime_extract/grey // What core is inside the slime, and what you get from the processor.
+ var/reagent_injected = null // Some slimes inject reagents on attack. This tells the game what reagent to use.
+ var/injection_amount = 5 // This determines how much.
+ var/mood = ":3" // Icon to use to display 'mood', as an overlay.
+
+ can_enter_vent_with = list(/obj/item/clothing/head)
+
+/datum/say_list/slime
+ speak = list("Blorp...", "Blop...")
+ emote_see = list("bounces", "jiggles", "sways")
+ emote_hear = list("squishes")
+
+/mob/living/simple_mob/slime/initialize()
+ verbs += /mob/living/proc/ventcrawl
+ update_mood()
+ update_icon()
+ glow_color = color
+ return ..()
+
+/mob/living/simple_mob/slime/Destroy()
+ if(hat)
+ drop_hat()
+ return ..()
+
+/mob/living/simple_mob/slime/update_icon()
+ ..() // Do the regular stuff first.
+
+ var/mutable_appearance/MA = new(src)
+
+ if(stat != DEAD)
+ // General slime shine.
+ var/image/I = image(icon, src, "slime light")
+ I.appearance_flags = RESET_COLOR
+ MA.overlays += I
+
+ // 'Shiny' overlay, for gemstone-slimes.
+ if(shiny)
+ I = image(icon, src, "slime shiny")
+ I.appearance_flags = RESET_COLOR
+ MA.overlays += I
+
+ // Mood overlay.
+ I = image(icon, src, "aslime-[mood]")
+ I.appearance_flags = RESET_COLOR
+ MA.overlays += I
+
+ // Hat simulator.
+ if(hat)
+ var/hat_state = hat.item_state ? hat.item_state : hat.icon_state
+ var/image/I = image('icons/mob/head.dmi', src, hat_state)
+ I.pixel_y = -7 // Slimes are small.
+ I.appearance_flags = RESET_COLOR
+ MA.overlays += I
+
+ appearance = MA
+
+// Controls the 'mood' overlay. Overrided in subtypes for specific behaviour.
+/mob/living/simple_mob/slime/proc/update_mood()
+ mood = "angry" // This is to avoid another override in the /feral subtype.
+
+/mob/living/simple_mob/slime/proc/unify()
+ unity = TRUE
+
+// Interface override, because slimes are supposed to attack other slimes of different color regardless of faction
+// (unless Unified, of course).
+/mob/living/simple_mob/slime/IIsAlly(mob/living/L)
+ . = ..()
+ if(.) // Need to do an additional check if its another slime.
+ if(istype(L, /mob/living/simple_mob/slime))
+ var/mob/living/simple_mob/slime/S = L
+ if(S.slime_color != src.slime_color)
+ if(S.unity || src.unity)
+ return TRUE
+ return FALSE
+ // The other stuff was already checked in parent proc, and the . variable will implicitly return the correct value.
+
+// Slimes regenerate passively.
+/mob/living/simple_mob/slime/handle_special()
+ adjustOxyLoss(-1)
+ adjustToxLoss(-1)
+ adjustFireLoss(-1)
+ adjustCloneLoss(-1)
+ adjustBruteLoss(-1)
+
+/mob/living/simple_mob/slime/water_act(amount) // This is called if a slime enters a water tile.
+ adjustBruteLoss(40 * amount)
+
+
+// Clicked on by empty hand.
+/mob/living/simple_mob/slime/attack_hand(mob/living/L)
+ if(L.a_intent == I_HELP && hat)
+ remove_hat(L)
+ else
+ ..()
+
+// Clicked on while holding an object.
+/mob/living/simple_mob/slime/attackby(obj/item/I, mob/user)
+ if(istype(I, /obj/item/clothing/head)) // Handle hat simulator.
+ give_hat(I, user)
+ return
+
+ // Otherwise they're probably fighting the slime.
+ if(prob(25))
+ visible_message(span("warning", "\The [user]'s [I] passes right through \the [src]!"))
+ user.setClickCooldown(user.get_attack_speed(I))
+ return
+ ..()
+
+// Called when hit with an active slimebaton (or xeno taser).
+// Subtypes react differently.
+/mob/living/simple_mob/slime/proc/slimebatoned(mob/living/user, amount)
+ return
+
+// Hat simulator
+/mob/living/simple_mob/slime/proc/give_hat(var/obj/item/clothing/head/new_hat, var/mob/living/user)
+ if(!istype(new_hat))
+ to_chat(user, span("warning", "\The [new_hat] isn't a hat."))
+ return
+ if(hat)
+ to_chat(user, span("warning", "\The [src] is already wearing \a [hat]."))
+ return
+ else
+ user.drop_item(new_hat)
+ hat = new_hat
+ new_hat.forceMove(src)
+ to_chat(user, span("notice", "You place \a [new_hat] on \the [src]. How adorable!"))
+ update_icon()
+ return
+
+/mob/living/simple_mob/slime/proc/remove_hat(var/mob/living/user)
+ if(!hat)
+ to_chat(user, "\The [src] doesn't have a hat to remove.")
+ else
+ hat.forceMove(get_turf(src))
+ user.put_in_hands(hat)
+ to_chat(user, "You take away \the [src]'s [hat.name]. How mean.")
+ hat = null
+ update_icon()
+
+/mob/living/simple_mob/slime/proc/drop_hat()
+ if(!hat)
+ return
+ hat.forceMove(get_turf(src))
+ hat = null
+ update_icon()
+
+/mob/living/simple_mob/slime/speech_bubble_appearance()
+ return "slime"
+
diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/combat.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/combat.dm
new file mode 100644
index 0000000000..2cda9bfc7e
--- /dev/null
+++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/combat.dm
@@ -0,0 +1,76 @@
+// Code for slimes attacking other things.
+
+// Slime attacks change based on intent.
+/mob/living/simple_mob/slime/xenobio/apply_attack(mob/living/L, damage_to_do)
+ if(istype(L))
+ switch(a_intent)
+ if(I_HELP) // This shouldn't happen but just in case.
+ return FALSE
+
+ if(I_DISARM)
+ var/stun_power = between(0, power_charge + rand(0, 3), 10)
+
+ if(ishuman(L))
+ var/mob/living/carbon/human/H = L
+ stun_power *= max(H.species.siemens_coefficient, 0)
+
+ if(prob(stun_power * 10)) // Try an electric shock.
+ power_charge = max(0, power_charge - 3)
+ L.visible_message(
+ span("danger", "\The [src] has shocked \the [L]!"),
+ span("danger", "\The [src] has shocked you!")
+ )
+ playsound(src, 'sound/weapons/Egloves.ogg', 75, 1)
+ L.Weaken(4)
+ L.Stun(4)
+ do_attack_animation(L)
+ if(L.buckled)
+ L.buckled.unbuckle_mob() // To prevent an exploit where being buckled prevents slimes from jumping on you.
+ L.stuttering = max(L.stuttering, stun_power)
+
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+ s.set_up(5, 1, L)
+ s.start()
+
+ if(prob(stun_power * 10) && stun_power >= 8)
+ L.adjustFireLoss(power_charge * rand(1, 2))
+ return FALSE
+
+ else if(prob(20)) // Try to do a regular disarm attack.
+ L.visible_message(
+ span("danger", "\The [src] has pounced at \the [L]!"),
+ span("danger", "\The [src] has pounced at you!")
+ )
+ playsound(src, 'sound/weapons/thudswoosh.ogg', 75, 1)
+ L.Weaken(2)
+ do_attack_animation(L)
+ if(L.buckled)
+ L.buckled.unbuckle_mob() // To prevent an exploit where being buckled prevents slimes from jumping on you.
+ return FALSE
+
+ else // Failed to do anything this time.
+ L.visible_message(
+ span("warning", "\The [src] has tried to pounce at \the [L]!"),
+ span("warning", "\The [src] has tried to pounce at you!")
+ )
+ playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1)
+ do_attack_animation(L)
+ return FALSE
+
+ if(I_GRAB)
+ start_consuming(L)
+ return FALSE
+
+ if(I_HURT)
+ return ..() // Regular stuff.
+ else
+ return ..() // Do the regular stuff if we're hitting a window/mech/etc.
+
+/mob/living/simple_mob/slime/xenobio/apply_melee_effects(mob/living/L)
+ if(istype(L) && a_intent == I_HURT)
+ // Pump them full of toxins, if able.
+ if(L.reagents && L.can_inject() && reagent_injected)
+ L.reagents.add_reagent(reagent_injected, injection_amount)
+
+ // Feed off of their flesh, if able.
+ consume(L, 5)
diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm
new file mode 100644
index 0000000000..d158493246
--- /dev/null
+++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/consumption.dm
@@ -0,0 +1,169 @@
+// Handles hunger, starvation, growth, and eatting humans.
+
+// Might be best to make this a /mob/living proc and override.
+/mob/living/simple_mob/slime/xenobio/proc/adjust_nutrition(input)
+ nutrition = between(0, nutrition + input, get_max_nutrition())
+
+ if(input > 0)
+ // Gain around one level per 50 nutrition.
+ if(prob(input * 2))
+ power_charge = min(power_charge++, 10)
+ if(power_charge == 10)
+ adjustToxLoss(-10)
+
+ // Heal 1 point of damage per 5 nutrition coming in.
+ adjustBruteLoss(-input * 0.2)
+ adjustFireLoss(-input * 0.2)
+ adjustToxLoss(-input * 0.2)
+ adjustOxyLoss(-input * 0.2)
+ adjustCloneLoss(-input * 0.2)
+
+
+/mob/living/simple_mob/slime/xenobio/proc/get_max_nutrition() // Can't go above it
+ return is_adult ? 1200 : 1000
+
+/mob/living/simple_mob/slime/xenobio/proc/get_grow_nutrition() // Above it we grow, below it we can eat
+ return is_adult ? 1000 : 800
+
+/mob/living/simple_mob/slime/xenobio/proc/get_hunger_nutrition() // Below it we will always eat
+ return is_adult ? 600 : 500
+
+/mob/living/simple_mob/slime/xenobio/proc/get_starve_nutrition() // Below it we will eat before everything else
+ return is_adult ? 300 : 200
+
+// Called by Life().
+/mob/living/simple_mob/slime/xenobio/proc/handle_nutrition()
+ if(harmless)
+ return
+
+ if(prob(15))
+ adjust_nutrition(is_adult ? -2 : -1) // Adult slimes get hungry faster.
+
+ if(nutrition <= get_starve_nutrition())
+ handle_starvation()
+
+ else if(nutrition >= get_grow_nutrition() && amount_grown < 10)
+ adjust_nutrition(-20)
+ amount_grown = between(0, amount_grown + 1, 10)
+
+// Called if above proc happens while below a nutrition threshold.
+/mob/living/simple_mob/slime/xenobio/proc/handle_starvation()
+ if(nutrition < get_starve_nutrition() && !client) // if a slime is starving, it starts losing its friends
+ if(friends.len && prob(1))
+ var/mob/nofriend = pick(friends)
+ if(nofriend)
+ friends -= nofriend
+ say("[nofriend]... food now...")
+
+ if(nutrition <= 0)
+ adjustToxLoss(rand(1,3))
+ if(client && prob(5))
+ to_chat(src, span("danger", "You are starving!"))
+
+
+/mob/living/simple_mob/slime/xenobio/proc/handle_consumption()
+ if(victim && !stat)
+ if(istype(victim) && consume(victim, 20))
+ if(prob(25))
+ to_chat(src, span("notice", "You continue absorbing \the [victim]."))
+
+ else
+ var/list/feedback = list(
+ "This subject is incompatable",
+ "This subject does not have a life energy",
+ "This subject is empty",
+ "I am not satisfied",
+ "I can not feed from this subject",
+ "I do not feel nourished",
+ "This subject is not food"
+ )
+ to_chat(src, span("warning", "[pick(feedback)]..."))
+ stop_consumption()
+
+ if(victim)
+ victim.updatehealth()
+
+ else
+ stop_consumption()
+
+/mob/living/simple_mob/slime/xenobio/proc/start_consuming(mob/living/L)
+ if(!can_consume(L))
+ return
+ if(!Adjacent(L))
+ return
+
+ step_towards(src, L) // Get on top of them to feed.
+ if(loc != L.loc)
+ return
+
+ if(L.buckle_mob(src, forced = TRUE))
+ victim = L
+ update_icon()
+ set_AI_busy(TRUE) // Don't want the AI to interfere with eatting.
+ victim.visible_message(
+ span("danger", "\The [src] latches onto \the [victim]!"),
+ span("critical", "\The [src] latches onto you!")
+ )
+
+/mob/living/simple_mob/slime/xenobio/proc/stop_consumption(mob/living/L)
+ if(!victim)
+ return
+ victim.unbuckle_mob()
+ victim.visible_message(
+ span("notice", "\The [src] slides off of [victim]!"),
+ span("notice", "\The [src] slides off of you!")
+ )
+ victim = null
+ update_icon()
+ set_AI_busy(FALSE) // Resume normal operations.
+
+/mob/living/simple_mob/slime/xenobio/proc/can_consume(mob/living/L)
+ if(!L || !istype(L))
+ to_chat(src, "This subject is incomparable...")
+ return FALSE
+ if(harmless)
+ to_chat(src, "I am pacified... I cannot eat...")
+ return FALSE
+ if(L.mob_class & MOB_CLASS_SLIME)
+ to_chat(src, "I cannot feed on other slimes...")
+ return FALSE
+ if(L.isSynthetic())
+ to_chat(src, "This subject is not biological...")
+ return FALSE
+ if(L.getarmor(null, "bio") >= 75)
+ to_chat(src, "I cannot reach this subject's biological matter...")
+ return FALSE
+ if(!Adjacent(L))
+ to_chat(src, "This subject is too far away...")
+ return FALSE
+ if(L.getCloneLoss() >= L.getMaxHealth() * 1.5)
+ to_chat(src, "This subject does not have an edible life energy...")
+ return FALSE
+ if(L.has_buckled_mobs())
+ for(var/A in L.buckled_mobs)
+ if(istype(A, /mob/living/simple_mob/slime/xenobio))
+ if(A != src)
+ to_chat(src, "\The [A] is already feeding on this subject...")
+ return FALSE
+ return TRUE
+
+// This does the actual damage, as well as give nutrition and heals.
+// Assuming no bio armor, calling consume(10) will result in;
+// 6 clone damage to victim
+// 4 tox damage to victim.
+// 25 nutrition for the slime.
+// 2 points of damage healed on the slime (as a result of the nutrition).
+// 50% of giving +1 charge to the slime (same as above).
+/mob/living/simple_mob/slime/xenobio/proc/consume(mob/living/victim, amount)
+ if(can_consume(victim))
+ var/armor_modifier = abs((victim.getarmor(null, "bio") / 100) - 1)
+ var/damage_done = amount * armor_modifier
+ if(damage_done > 0)
+ victim.adjustCloneLoss(damage_done * 0.6)
+ victim.adjustToxLoss(damage_done * 0.4)
+ adjust_nutrition(damage_done * 5)
+ Beam(victim, icon_state = "slime_consume", time = 8)
+ to_chat(src, span("notice", "You absorb some biomaterial from \the [victim]."))
+ to_chat(victim, span("danger", "\The [src] consumes some of your flesh!"))
+ return TRUE
+ return FALSE
diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/defense.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/defense.dm
new file mode 100644
index 0000000000..d5190f0abb
--- /dev/null
+++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/defense.dm
@@ -0,0 +1,88 @@
+// Contains code for slimes getting attacked, beat, touched, etc, and reacting to that.
+
+// Clicked on by empty hand.
+// Handles trying to wrestle a slime off of someone being eatten.
+/mob/living/simple_mob/slime/xenobio/attack_hand(mob/living/L)
+ if(victim) // Are we eating someone?
+ var/fail_odds = 30
+ if(victim == L) // Harder to get the slime off if it's you that is being eatten.
+ fail_odds = 60
+
+ if(prob(fail_odds))
+ visible_message(span("warning", "\The [L] attempts to wrestle \the [name] off!"))
+ playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
+
+ else
+ visible_message(span("warning", "\The [L] manages to wrestle \the [name] off!"))
+ playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
+
+ if(prob(40))
+ adjust_discipline(1) // Do this here so that it will be justified discipline.
+ stop_consumption()
+ step_away(src, L)
+
+ else
+ ..()
+
+// Handles the actual harming by a melee weapon.
+/mob/living/simple_mob/slime/xenobio/hit_with_weapon(obj/item/I, mob/living/user, effective_force, hit_zone)
+ ..() // Apply damage and etc.
+ if(!stat && effective_force > 0)
+ if(!is_justified_to_discipline()) // Wow, buddy, why am I getting attacked??
+ adjust_discipline(1) // This builds resentment due to being unjustified.
+
+ if(user in friends) // Friend attacking us for no reason.
+ if(prob(25))
+ friends -= user
+ say("[user]... not friend...")
+
+ else // We're actually being bad.
+ var/prob_to_back_down = round(effective_force)
+ if(is_adult)
+ prob_to_back_down /= 2
+ if(prob(prob_to_back_down))
+ adjust_discipline(2) // Justified.
+
+// Shocked grilles don't hurt slimes, and in fact give them charge.
+/mob/living/simple_mob/slime/xenobio/electrocute_act(shock_damage, obj/source, siemens_coeff = 1.0, def_zone = null)
+ power_charge = between(0, power_charge + round(shock_damage / 10), 10)
+ to_chat(src, span("notice", "\The [source] shocks you, and it charges you."))
+
+// Getting slimebatoned/xenotased.
+/mob/living/simple_mob/slime/xenobio/slimebatoned(mob/living/user, amount)
+ Weaken(amount)
+ adjust_discipline(round(amount/2))
+
+/*
+
+/mob/living/simple_animal/slime/hit_with_weapon(obj/item/O, mob/living/user, var/effective_force, var/hit_zone)
+ ..()
+ if(!stat)
+ if(O.force > 0 && discipline && !rabid) // wow, buddy, why am I getting attacked??
+ adjust_discipline(1)
+ return
+ if(O.force >= 3)
+ if(victim || target_mob) // We've been a bad slime.
+ if(is_adult)
+ if(prob(5 + round(O.force / 2)) )
+ if(prob(80) && !client)
+ adjust_discipline(2)
+ if(user)
+ step_away(src, user)
+ else
+ if(prob(10 + O.force * 2))
+ if(prob(80) && !client)
+ adjust_discipline(2)
+ if(user)
+ step_away(src, user)
+ else
+ if(user in friends) // Friend attacking us for no reason.
+ if(prob(25))
+ friends -= user
+ say("[user]... not friend...")
+
+// Shocked grilles don't hurt slimes, and in fact give them charge.
+/mob/living/simple_animal/slime/electrocute_act(var/shock_damage, var/obj/source, var/siemens_coeff = 1.0, var/def_zone = null)
+ power_charge = between(0, power_charge + round(shock_damage / 10), 10)
+ to_chat(src, "\The [source] shocks you, and it charges you.")
+*/
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/discipline.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/discipline.dm
new file mode 100644
index 0000000000..7980b966ee
--- /dev/null
+++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/discipline.dm
@@ -0,0 +1,22 @@
+// Handles the subjugation of slimes by force.
+
+/mob/living/simple_mob/slime/xenobio/proc/adjust_discipline(amount, silent)
+ if(amount > 0)
+ to_chat(src, span("warning", "You've been disciplined!"))
+ if(has_AI())
+ var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder
+ AI.adjust_discipline(amount, silent)
+
+
+/mob/living/simple_mob/slime/xenobio/proc/is_justified_to_discipline()
+ if(victim) // Punish if eating someone that isn't a monkey.
+ if(ishuman(victim))
+ var/mob/living/carbon/human/H = victim
+ if(istype(H.species, /datum/species/monkey))
+ return FALSE
+ return TRUE
+
+ else if(has_AI()) // Now for thoughtcrimes.
+ var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder
+ return AI.is_justified_to_discipline() // Will return true if targeting a non-monkey.
+ return FALSE
diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm
new file mode 100644
index 0000000000..660805f1bc
--- /dev/null
+++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/subtypes.dm
@@ -0,0 +1,252 @@
+// Here are where all the other colors of slime live.
+// They will generally fight each other if not Unified, meaning the xenobiologist has to seperate them.
+
+// Tier 1.
+
+/mob/living/simple_mob/slime/xenobio/purple
+ desc = "This slime is rather toxic to handle, as it is poisonous."
+ color = "#CC23FF"
+ slime_color = "purple"
+ coretype = /obj/item/slime_extract/purple
+ reagent_injected = "toxin"
+
+ description_info = "This slime spreads a toxin when it attacks. A biosuit or other thick armor can protect from the toxic attack."
+ player_msg = "You inject a harmful toxin when attacking."
+
+ slime_mutation = list(
+// /mob/living/simple_animal/slime/dark_purple,
+// /mob/living/simple_animal/slime/dark_blue,
+// /mob/living/simple_animal/slime/green,
+// /mob/living/simple_animal/slime
+ )
+
+/mob/living/simple_mob/slime/xenobio/orange
+ desc = "This slime is known to be flammable and can ignite enemies."
+ color = "#FFA723"
+ slime_color = "orange"
+ coretype = /obj/item/slime_extract/orange
+ melee_damage_lower = 5
+ melee_damage_upper = 5
+
+ description_info = "Attacks from this slime will burn you, and can ignite you. A firesuit can protect from the burning attacks of this slime."
+ player_msg = "You inflict burning attacks, which causes additional damage, makes the target more flammable, and has a chance to ignite them."
+
+ slime_mutation = list(
+// /mob/living/simple_animal/slime/dark_purple,
+// /mob/living/simple_animal/slime/yellow,
+// /mob/living/simple_animal/slime/red,
+// /mob/living/simple_animal/slime
+ )
+
+/mob/living/simple_mob/slime/xenobio/orange/apply_melee_effects(atom/A)
+ ..()
+ if(isliving(A))
+ var/mob/living/L = A
+ L.inflict_heat_damage(is_adult ? 20 : 10)
+ to_chat(src, span("span", "You burn \the [L]."))
+ to_chat(L, span("danger", "You've been burned by \the [src]!"))
+ L.adjust_fire_stacks(1)
+ if(prob(12))
+ L.IgniteMob()
+
+
+/mob/living/simple_mob/slime/xenobio/metal
+ desc = "This slime is a lot more resilient than the others, due to having a metamorphic metallic and sloped surface."
+ color = "#5F5F5F"
+ slime_color = "metal"
+ shiny = TRUE
+ coretype = /obj/item/slime_extract/metal
+
+ description_info = "This slime is a lot more durable and tough to damage than the others. It also seems to provoke others to attack it over others."
+ player_msg = "You are more resilient and armored than more slimes. Your attacks will also encourage less intelligent enemies to focus on you."
+
+ maxHealth = 250
+ maxHealth_adult = 350
+
+ // The sloped armor.
+ // It's resistant to most weapons (but a spraybottle still kills it rather fast).
+ armor = list(
+ "melee" = 25,
+ "bullet" = 25,
+ "laser" = 25,
+ "energy" = 50,
+ "bomb" = 80,
+ "bio" = 100,
+ "rad" = 100
+ )
+
+ armor_soak = list(
+ "melee" = 5,
+ "bullet" = 5,
+ "laser" = 5,
+ "energy" = 0,
+ "bomb" = 0,
+ "bio" = 0,
+ "rad" = 0
+ )
+
+ slime_mutation = list(
+// /mob/living/simple_animal/slime/silver,
+// /mob/living/simple_animal/slime/yellow,
+// /mob/living/simple_animal/slime/gold,
+// /mob/living/simple_animal/slime
+ )
+
+/mob/living/simple_mob/slime/xenobio/metal/apply_melee_effects(atom/A)
+ ..()
+ if(isliving(A))
+ var/mob/living/L = A
+ L.taunt(src, TRUE) // We're the party tank now.
+
+// Tier 2
+
+/mob/living/simple_mob/slime/xenobio/yellow
+ desc = "This slime is very conductive, and is known to use electricity as a means of defense moreso than usual for slimes."
+ color = "#FFF423"
+ slime_color = "yellow"
+ coretype = /obj/item/slime_extract/yellow
+ melee_damage_lower = 5
+ melee_damage_upper = 5
+
+ projectiletype = /obj/item/projectile/beam/lightning/slime
+ projectilesound = 'sound/weapons/gauss_shoot.ogg' // Closest thing to a 'thunderstrike' sound we have.
+ glow_toggle = TRUE
+
+ description_info = "This slime will fire ranged lightning attacks at enemies if they are at range, inflict shocks upon entities they attack, \
+ and generate electricity for their stun attack faster than usual. Insulative or reflective armor can protect from these attacks."
+ player_msg = "You have a ranged electric attack. You also shock enemies you attack, and your electric stun attack charges passively."
+
+ slime_mutation = list(
+// /mob/living/simple_animal/slime/bluespace,
+// /mob/living/simple_animal/slime/bluespace,
+// /mob/living/simple_animal/slime/metal,
+// /mob/living/simple_animal/slime/orange
+ )
+
+/mob/living/simple_mob/slime/xenobio/yellow/apply_melee_effects(atom/A)
+ ..()
+ if(isliving(A))
+ var/mob/living/L = A
+ L.inflict_shock_damage(is_adult ? 20 : 10)
+ to_chat(src, span("span", "You shock \the [L]."))
+ to_chat(L, span("danger", "You've been shocked by \the [src]!"))
+
+/mob/living/simple_mob/slime/xenobio/yellow/handle_special()
+ if(stat == CONSCIOUS)
+ if(prob(25))
+ power_charge = between(0, power_charge + 1, 10)
+ ..()
+
+/obj/item/projectile/beam/lightning/slime
+ power = 10
+
+
+/mob/living/simple_mob/slime/xenobio/dark_purple
+ desc = "This slime produces ever-coveted phoron. Risky to handle but very much worth it."
+ color = "#660088"
+ slime_color = "dark purple"
+ coretype = /obj/item/slime_extract/dark_purple
+ reagent_injected = "phoron"
+
+ description_info = "This slime applies phoron to enemies it attacks. A biosuit or other thick armor can protect from the toxic attack. \
+ If hit with a burning attack, it will erupt in flames."
+ player_msg = "You inject phoron into enemies you attack.
\
+ You will erupt into flames if harmed by fire!"
+
+ slime_mutation = list(
+// /mob/living/simple_animal/slime/purple,
+// /mob/living/simple_animal/slime/orange,
+// /mob/living/simple_animal/slime/ruby,
+// /mob/living/simple_animal/slime/ruby
+ )
+
+/mob/living/simple_mob/slime/xenobio/dark_purple/proc/ignite()
+ visible_message(span("critical", "\The [src] erupts in an inferno!"))
+ for(var/turf/simulated/target_turf in view(2, src))
+ target_turf.assume_gas("phoron", 30, 1500+T0C)
+ spawn(0)
+ target_turf.hotspot_expose(1500+T0C, 400)
+ qdel(src)
+
+/mob/living/simple_mob/slime/xenobio/dark_purple/ex_act(severity)
+ log_and_message_admins("[src] ignited due to a chain reaction with an explosion.")
+ ignite()
+
+/mob/living/simple_mob/slime/xenobio/dark_purple/fire_act(datum/gas_mixture/air, temperature, volume)
+ log_and_message_admins("[src] ignited due to exposure to fire.")
+ ignite()
+
+/mob/living/simple_mob/slime/xenobio/dark_purple/bullet_act(var/obj/item/projectile/P, var/def_zone)
+ if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security.
+ log_and_message_admins("[src] ignited due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].")
+ ignite()
+ else
+ ..()
+
+/mob/living/simple_mob/slime/xenobio/dark_purple/attackby(var/obj/item/weapon/W, var/mob/user)
+ if(istype(W) && W.force && W.damtype == BURN)
+ log_and_message_admins("[src] ignited due to being hit with a burning weapon ([W]) by [key_name(user)].")
+ ignite()
+ else
+ ..()
+
+
+
+/mob/living/simple_mob/slime/xenobio/dark_blue
+ desc = "This slime makes other entities near it feel much colder, and is more resilient to the cold. It tends to kill other slimes rather quickly."
+ color = "#2398FF"
+ glows = TRUE
+ slime_color = "dark blue"
+ coretype = /obj/item/slime_extract/dark_blue
+ melee_damage_lower = 5
+ melee_damage_upper = 5
+
+
+ description_info = "This slime is immune to the cold, however water will still kill it. Its attacks will \
+ also chill you, causing additional harm. A winter coat or other cold-resistant clothing can protect from the chill."
+ player_msg = "You are immune to the cold, inflict additional cold damage on attack, and cause nearby entities to become colder."
+
+ slime_mutation = list(
+// /mob/living/simple_animal/slime/purple,
+// /mob/living/simple_animal/slime/blue,
+// /mob/living/simple_animal/slime/cerulean,
+// /mob/living/simple_animal/slime/cerulean
+ )
+
+ minbodytemp = 0
+ cold_damage_per_tick = 0
+ cold_resist = 1
+
+/mob/living/simple_mob/slime/xenobio/dark_blue/handle_special()
+ if(stat != DEAD)
+ cold_aura()
+ ..()
+
+/mob/living/simple_mob/slime/xenobio/dark_blue/proc/cold_aura()
+ for(var/mob/living/L in view(2, src))
+ if(L == src)
+ continue
+ chill(L)
+
+ var/turf/T = get_turf(src)
+ var/datum/gas_mixture/env = T.return_air()
+ if(env)
+ env.add_thermal_energy(-10 * 1000)
+
+/mob/living/simple_mob/slime/xenobio/dark_blue/apply_melee_effects(atom/A)
+ ..()
+ if(isliving(A))
+ var/mob/living/L = A
+ chill(L)
+ to_chat(src, span("span", "You chill \the [L]."))
+ to_chat(L, span("danger", "You've been chilled by \the [src]!"))
+
+
+/mob/living/simple_mob/slime/xenobio/dark_blue/proc/chill(mob/living/L)
+ L.inflict_cold_damage(is_adult ? 20 : 10)
+ if(!L.get_cold_protection() * 100) // Having complete protection always saves you, otherwise there's a chance to get this modifier.
+ add_modifier(/datum/modifier/chilled, 10 SECONDS)
+
+/*
+
+*/
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm
new file mode 100644
index 0000000000..2ba5b79ed7
--- /dev/null
+++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm
@@ -0,0 +1,260 @@
+// These slimes have the mechanics xenobiologists care about, such as reproduction, mutating into new colors, and being able to submit through fear.
+
+/mob/living/simple_mob/slime/xenobio
+ desc = "The most basic of slimes. The grey slime has no remarkable qualities, however it remains one of the most useful colors for scientists."
+ layer = MOB_LAYER + 1 // Need them on top of other mobs or it looks weird when consuming something.
+ ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime // This should never be changed for xenobio slimes.
+ var/is_adult = FALSE // Slimes turn into adults when fed enough. Adult slimes are somewhat stronger, and can reproduce if fed enough.
+ var/maxHealth_adult = 200
+ var/power_charge = 0 // Disarm attacks can shock someone if high/lucky enough.
+ var/mob/living/victim = null // the person the slime is currently feeding on
+ var/rainbow_core_candidate = TRUE // If false, rainbow cores cannot make this type randomly.
+ var/mutation_chance = 25 // Odds of spawning as a new color when reproducing. Can be modified by certain xenobio products. Carried across generations of slimes.
+ var/list/slime_mutation = list(
+ /mob/living/simple_mob/slime/xenobio/orange,
+ /mob/living/simple_mob/slime/xenobio/metal,
+ /mob/living/simple_mob/slime/xenobio/blue,
+ /mob/living/simple_mob/slime/xenobio/purple
+ )
+ var/amount_grown = 0 // controls how long the slime has been overfed, if 10, grows or reproduces
+ var/number = 0 // This is used to make the slime semi-unique for indentification.
+ var/harmless = FALSE // Set to true when pacified. Makes the slime harmless, not get hungry, and not be able to grow/reproduce.
+
+/mob/living/simple_mob/slime/xenobio/initialize()
+ ASSERT(ispath(ai_holder_type, /datum/ai_holder/simple_mob/xenobio_slime))
+ number = rand(1, 1000)
+ update_name()
+ return ..()
+
+/mob/living/simple_mob/slime/xenobio/Destroy()
+ if(victim)
+ stop_consumption() // Unbuckle us from our victim.
+ return ..()
+
+/mob/living/simple_mob/slime/xenobio/update_icon()
+ icon_living = "[icon_state_override ? "[icon_state_override] slime" : "slime"] [is_adult ? "adult" : "baby"][victim ? " eating" : ""]"
+ icon_dead = "[icon_state_override ? "[icon_state_override] slime" : "slime"] [is_adult ? "adult" : "baby"] dead"
+ icon_rest = icon_dead
+ ..() // This will apply the correct icon_state and do the other overlay-related things.
+
+
+/mob/living/simple_mob/slime/xenobio/handle_special()
+ if(stat != DEAD)
+ handle_nutrition()
+
+ if(victim)
+ handle_consumption()
+
+ handle_stuttering() // ??
+
+ ..()
+
+/mob/living/simple_mob/slime/xenobio/examine(mob/user)
+ ..()
+ if(hat)
+ to_chat(user, "It is wearing \a [hat].")
+
+ if(stat == DEAD)
+ to_chat(user, "It appears to be dead.")
+ else if(incapacitated(INCAPACITATION_DISABLED))
+ to_chat(user, "It appears to be incapacitated.")
+ else if(harmless)
+ to_chat(user, "It appears to have been pacified.")
+ else
+ if(has_AI())
+ var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder
+ if(AI.rabid)
+ to_chat(user, "It seems very, very angry and upset.")
+ else if(AI.obedience >= 5)
+ to_chat(user, "It looks rather obedient.")
+ else if(AI.discipline)
+ to_chat(user, "It has been subjugated by force, at least for now.")
+
+/mob/living/simple_mob/slime/xenobio/proc/make_adult()
+ if(is_adult)
+ return
+
+ is_adult = TRUE
+ melee_damage_lower = round(melee_damage_lower * 2) // 20
+ melee_damage_upper = round(melee_damage_upper * 2) // 30
+ maxHealth = maxHealth_adult
+ amount_grown = 0
+ update_icon()
+ update_name()
+
+/mob/living/simple_mob/slime/xenobio/proc/update_name()
+ if(harmless) // Docile slimes are generally named, so we shouldn't mess with it.
+ return
+ name = "[slime_color] [is_adult ? "adult" : "baby"] [initial(name)] ([number])"
+ real_name = name
+
+/mob/living/simple_mob/slime/xenobio/update_mood()
+ var/old_mood = mood
+ if(incapacitated(INCAPACITATION_DISABLED))
+ mood = "sad"
+ else if(harmless)
+ mood = ":33"
+ else if(has_AI())
+ var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder
+ if(AI.rabid)
+ mood = "angry"
+ else if(AI.target)
+ mood = "mischevous"
+ else if(AI.discipline)
+ mood = "pout"
+ else
+ mood = ":3"
+ else
+ mood = ":3"
+
+ if(old_mood != mood)
+ update_icon()
+
+/mob/living/simple_mob/slime/xenobio/proc/enrage()
+ if(harmless)
+ return
+ if(has_AI())
+ var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder
+ AI.enrage()
+
+/mob/living/simple_mob/slime/xenobio/proc/pacify()
+ harmless = TRUE
+ if(has_AI())
+ var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder
+ AI.pacify()
+
+ // If for whatever reason the mob AI (or player) decides to try to attack something anyways.
+ melee_damage_upper = 0
+ melee_damage_lower = 0
+
+ update_mood()
+
+
+// These are verbs so that player slimes can evolve/split.
+/mob/living/simple_mob/slime/xenobio/verb/evolve()
+ set category = "Slime"
+ set desc = "This will let you evolve from baby to adult slime."
+
+ if(stat)
+ to_chat(src, span("warning", "I must be conscious to do this..."))
+ return
+
+ if(harmless)
+ to_chat(src, span("warning", "I have been pacified. I cannot evolve..."))
+ return
+
+ if(!is_adult)
+ if(amount_grown >= 10)
+ make_adult()
+ else
+ to_chat(src, span("warning", "I am not ready to evolve yet..."))
+ else
+ to_chat(src, span("warning", "I have already evolved..."))
+
+
+/mob/living/simple_mob/slime/xenobio/verb/reproduce()
+ set category = "Slime"
+ set desc = "This will make you split into four new slimes."
+
+ if(stat)
+ to_chat(src, span("warning", "I must be conscious to do this..."))
+ return
+
+ if(harmless)
+ to_chat(src, span("warning", "I have been pacified. I cannot reproduce..."))
+ return
+
+ if(is_adult)
+ if(amount_grown >= 10)
+ // Check if there's enough 'room' to split.
+ var/list/nearby_things = orange(1, src)
+ var/free_tiles = 0
+ for(var/turf/T in nearby_things)
+ var/free = TRUE
+ if(T.density) // No walls.
+ continue
+ for(var/atom/movable/AM in T)
+ if(AM.density)
+ free = FALSE
+ break
+
+ if(free)
+ free_tiles++
+
+ if(free_tiles < 3) // Three free tiles are needed, as four slimes are made and the 4th tile is from the center tile that the current slime occupies.
+ to_chat(src, span("warning", "It is too cramped here to reproduce..."))
+ return
+
+ var/list/babies = list()
+ for(var/i = 1 to 4)
+ babies.Add(make_new_slime())
+
+ var/mob/living/simple_mob/slime/new_slime = pick(babies)
+ new_slime.universal_speak = universal_speak
+ if(src.mind)
+ src.mind.transfer_to(new_slime)
+ else
+ new_slime.key = src.key
+ qdel(src)
+ else
+ to_chat(src, span("warning", "I am not ready to reproduce yet..."))
+ else
+ to_chat(src, span("warning", "I have not evolved enough to reproduce yet..."))
+
+// Used when reproducing or dying.
+/mob/living/simple_mob/slime/xenobio/proc/make_new_slime(var/desired_type)
+ var/t = src.type
+ if(desired_type)
+ t = desired_type
+// if(prob(mutation_chance / 10))
+// t = /mob/living/simple_mob/slime/xenobio/rainbow
+ else if(prob(mutation_chance) && slime_mutation.len)
+ t = slime_mutation[rand(1, slime_mutation.len)]
+ var/mob/living/simple_mob/slime/xenobio/baby = new t(loc)
+
+ // Handle 'inheriting' from parent slime.
+ baby.mutation_chance = mutation_chance
+ baby.power_charge = round(power_charge / 4)
+// baby.resentment = max(resentment - 1, 0)
+// if(!istype(baby, /mob/living/simple_mob/slime/xenobio/light_pink))
+// baby.discipline = max(discipline - 1, 0)
+// baby.obedience = max(obedience - 1, 0)
+// if(!istype(baby, /mob/living/simple_mob/slime/xenobio/rainbow))
+// baby.unity = unity
+ baby.faction = faction
+// baby.attack_same = attack_same
+ baby.friends = friends.Copy()
+// if(rabid)
+// baby.enrage()
+
+ step_away(baby, src)
+ return baby
+
+
+/mob/living/simple_mob/slime/xenobio/get_description_interaction()
+ var/list/results = list()
+
+ if(!stat)
+ results += "[desc_panel_image("slimebaton")]to stun the slime, if it's being bad."
+
+ results += ..()
+
+ return results
+
+/mob/living/simple_mob/slime/xenobio/get_description_info()
+ var/list/lines = list()
+ var/intro_line = "Slimes are generally the test subjects of Xenobiology, with different colors having different properties. \
+ They can be extremely dangerous if not handled properly."
+ lines.Add(intro_line)
+ lines.Add(null) // To pad the line breaks.
+
+ var/list/rewards = list()
+ for(var/potential_color in slime_mutation)
+ var/mob/living/simple_mob/slime/S = potential_color
+ rewards.Add(initial(S.slime_color))
+ var/reward_line = "This color of slime can mutate into [english_list(rewards)] colors, when it reproduces. It will do so when it has eatten enough."
+ lines.Add(reward_line)
+ lines.Add(null)
+
+ lines.Add(description_info)
+ return lines.Join("\n")
\ No newline at end of file
diff --git a/code/modules/xenobio/items/weapons.dm b/code/modules/xenobio/items/weapons.dm
index 8fb5f9eea6..bb735c35f2 100644
--- a/code/modules/xenobio/items/weapons.dm
+++ b/code/modules/xenobio/items/weapons.dm
@@ -1,6 +1,6 @@
/obj/item/weapon/melee/baton/slime
name = "slimebaton"
- desc = "A modified stun baton designed to stun slimes and other lesser xeno lifeforms for handling."
+ desc = "A modified stun baton designed to stun slimes and other lesser slimy xeno lifeforms for handling."
icon_state = "slimebaton"
item_state = "slimebaton"
slot_flags = SLOT_BELT
@@ -9,31 +9,30 @@
origin_tech = list(TECH_COMBAT = 2, TECH_BIO = 2)
agonyforce = 10 //It's not supposed to be great at stunning human beings.
hitcost = 48 //Less zap for less cost
- description_info = "This baton will stun a slime or other lesser lifeform for about five seconds, if hit with it while on."
+ description_info = "This baton will stun a slime or other slime-based lifeform for about five seconds, if hit with it while on."
-/obj/item/weapon/melee/baton/slime/attack(mob/M, mob/user, hit_zone)
- // Simple Animals.
- if(istype(M, /mob/living/simple_animal/slime) && status)
- var/mob/living/simple_animal/SA = M
- if(SA.intelligence_level <= SA_ANIMAL) // So it doesn't stun hivebots or syndies.
- SA.Weaken(5)
- if(isslime(SA))
- var/mob/living/simple_animal/slime/S = SA
- S.adjust_discipline(3)
+/obj/item/weapon/melee/baton/slime/attack(mob/living/L, mob/user, hit_zone)
+ if(istype(L) && status) // Is it on?
+ if(L.mob_class & MOB_CLASS_SLIME) // Are they some kind of slime? (Prommies might pass this check someday).
+ if(isslime(L))
+ var/mob/living/simple_mob/slime/S = L
+ S.slimebatoned(user, 5) // Feral and xenobio slimes will react differently to this.
+ else
+ L.Weaken(5)
+
+ // Now for prommies.
+ if(ishuman(L))
+ var/mob/living/carbon/human/H = L
+ if(H.species && H.species.name == SPECIES_PROMETHEAN)
+ var/agony_to_apply = 60 - agonyforce
+ H.apply_damage(agony_to_apply, HALLOSS)
- // Prometheans.
- if(ishuman(M))
- var/mob/living/carbon/human/H = M
- if(H.species && H.species.name == SPECIES_PROMETHEAN && status)
- var/agony_to_apply = 60 - agonyforce
- H.apply_damage(agony_to_apply, HALLOSS)
..()
-/obj/item/weapon/melee/baton/slime/loaded/New()
- ..()
+/obj/item/weapon/melee/baton/slime/loaded/initialize()
bcell = new/obj/item/weapon/cell/device(src)
update_icon()
- return
+ return ..()
// Research borg's version
@@ -61,9 +60,9 @@
charge_cost = 120 // Twice as many shots.
projectile_type = /obj/item/projectile/beam/stun/xeno
accuracy = 30 // Make it a bit easier to hit the slimes.
- description_info = "This gun will stun a slime or other lesser lifeform for about two seconds, if hit with the projectile it fires."
+ description_info = "This gun will stun a slime or other lesser slimy lifeform for about two seconds, if hit with the projectile it fires."
description_fluff = "An easy to use weapon designed by NanoTrasen, for NanoTrasen. This weapon is designed to subdue lesser \
- xeno lifeforms at a distance. It is ineffective at stunning larger lifeforms such as humanoids."
+ slime-based xeno lifeforms at a distance. It is ineffective at stunning non-slimy lifeforms such as humanoids."
/obj/item/weapon/gun/energy/taser/xeno/robot // Borg version
self_recharge = 1
@@ -71,7 +70,7 @@
recharge_time = 3
/obj/item/weapon/gun/energy/taser/xeno/sec //NT's corner-cutting option for their on-station security.
- desc = "An NT Mk30 NL retrofitted to fire beams for subduing non-humanoid xeno life forms."
+ desc = "An NT Mk30 NL retrofitted to fire beams for subduing non-humanoid slimy xeno life forms."
icon_state = "taserblue"
item_state = "taser"
projectile_type = /obj/item/projectile/beam/stun/xeno/weak
@@ -102,20 +101,17 @@
/obj/item/projectile/beam/stun/xeno/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null)
if(istype(target, /mob/living))
var/mob/living/L = target
+ if(L.mob_class & MOB_CLASS_SLIME)
+ if(isslime(L))
+ var/mob/living/simple_mob/slime/S = L
+ S.slimebatoned(firer, round(agony/2))
+ else
+ L.Weaken(round(agony/2))
- // Simple Animals.
- if(istype(L, /mob/living/simple_animal/slime))
- var/mob/living/simple_animal/SA = L
- if(SA.intelligence_level <= SA_ANIMAL) // So it doesn't stun hivebots or syndies.
- SA.Weaken(round(agony/2)) // Less powerful since its ranged, and therefore safer.
- if(isslime(SA))
- var/mob/living/simple_animal/slime/S = SA
- S.adjust_discipline(round(agony/2))
-
- // Prometheans.
if(ishuman(L))
var/mob/living/carbon/human/H = L
if(H.species && H.species.name == SPECIES_PROMETHEAN)
- if(agony == initial(agony))
+ if(agony == initial(agony)) // ??????
agony = round((14 * agony) - agony) //60-4 = 56, 56 / 4 = 14. Prior was flat 60 - agony of the beam to equate to 60.
+
..()
diff --git a/icons/effects/beam.dmi b/icons/effects/beam.dmi
index e7f61d605d..042350e15d 100644
Binary files a/icons/effects/beam.dmi and b/icons/effects/beam.dmi differ
diff --git a/icons/mob/modifier_effects.dmi b/icons/mob/modifier_effects.dmi
index ed8f297896..8a83de33ad 100644
Binary files a/icons/mob/modifier_effects.dmi and b/icons/mob/modifier_effects.dmi differ
diff --git a/polaris.dme b/polaris.dme
index 8eabc6b442..dcc5c04c88 100644
--- a/polaris.dme
+++ b/polaris.dme
@@ -1297,6 +1297,7 @@
#include "code\modules\ai\interfaces.dm"
#include "code\modules\ai\say_list.dm"
#include "code\modules\ai\aI_holder_subtypes\simple_mob_ai.dm"
+#include "code\modules\ai\aI_holder_subtypes\slime_xenobio_ai.dm"
#include "code\modules\alarm\alarm.dm"
#include "code\modules\alarm\alarm_handler.dm"
#include "code\modules\alarm\atmosphere_alarm.dm"
@@ -1956,6 +1957,13 @@
#include "code\modules\mob\living\simple_mob\on_click.dm"
#include "code\modules\mob\living\simple_mob\simple_hud.dm"
#include "code\modules\mob\living\simple_mob\simple_mob.dm"
+#include "code\modules\mob\living\simple_mob\subtypes\slime\slime.dm"
+#include "code\modules\mob\living\simple_mob\subtypes\slime\xenobio\combat.dm"
+#include "code\modules\mob\living\simple_mob\subtypes\slime\xenobio\consumption.dm"
+#include "code\modules\mob\living\simple_mob\subtypes\slime\xenobio\defense.dm"
+#include "code\modules\mob\living\simple_mob\subtypes\slime\xenobio\discipline.dm"
+#include "code\modules\mob\living\simple_mob\subtypes\slime\xenobio\subtypes.dm"
+#include "code\modules\mob\living\simple_mob\subtypes\slime\xenobio\xenobio.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\animal.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\farm animals\chicken.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\farm animals\cow.dm"
@@ -2540,7 +2548,7 @@
#include "code\ZAS\Zone.dm"
#include "interface\interface.dm"
#include "interface\skin.dmf"
-#include "maps\southern_cross\southern_cross.dm"
+#include "maps\example\example.dm"
#include "maps\submaps\space_submaps\space.dm"
#include "maps\submaps\surface_submaps\mountains\mountains.dm"
#include "maps\submaps\surface_submaps\mountains\mountains_areas.dm"