diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/_jungle_mobs.dm b/code/modules/mob/living/simple_animal/hostile/jungle/_jungle_mobs.dm
new file mode 100644
index 0000000000..c337344c90
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/_jungle_mobs.dm
@@ -0,0 +1,17 @@
+/mob/living/simple_animal/hostile/jungle
+ vision_range = 5
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ faction = list("jungle")
+ weather_immunities = list("acid")
+ obj_damage = 30
+ environment_smash = ENVIRONMENT_SMASH_WALLS
+ minbodytemp = 0
+ maxbodytemp = 450
+ response_help = "pokes"
+ response_disarm = "shoves"
+ response_harm = "strikes"
+ status_flags = NONE
+ a_intent = INTENT_HARM
+ see_in_dark = 4
+ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
+ mob_size = MOB_SIZE_LARGE
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
new file mode 100644
index 0000000000..491aca3233
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/leaper.dm
@@ -0,0 +1,263 @@
+#define PLAYER_HOP_DELAY 25
+
+//Huge, carnivorous toads that spit an immobilizing toxin at its victims before leaping onto them.
+//It has no melee attack, and its damage comes from the toxin in its bubbles and its crushing leap.
+//Its eyes will turn red to signal an imminent attack!
+/mob/living/simple_animal/hostile/jungle/leaper
+ name = "leaper"
+ desc = "Commonly referred to as 'leapers', the Geron Toad is a massive beast that spits out highly pressurized bubbles containing a unique toxin, knocking down its prey and then crushing it with its girth."
+ icon = 'icons/mob/jungle/leaper.dmi'
+ icon_state = "leaper"
+ icon_living = "leaper"
+ icon_dead = "leaper_dead"
+ maxHealth = 300
+ health = 300
+ ranged = TRUE
+ projectiletype = /obj/item/projectile/leaper
+ projectilesound = 'sound/weapons/pierce.ogg'
+ ranged_cooldown_time = 30
+ pixel_x = -16
+ layer = LARGE_MOB_LAYER
+ speed = 10
+ stat_attack = 1
+ robust_searching = 1
+ var/hopping = FALSE
+ var/hop_cooldown = 0 //Strictly for player controlled leapers
+ var/projectile_ready = FALSE //Stopping AI leapers from firing whenever they want, and only doing it after a hop has finished instead
+
+/obj/item/projectile/leaper
+ name = "leaper bubble"
+ icon_state = "leaper"
+ knockdown = 50
+ damage = 0
+ range = 7
+ hitsound = 'sound/effects/snap.ogg'
+ nondirectional_sprite = TRUE
+ impact_effect_type = /obj/effect/temp_visual/leaper_projectile_impact
+
+/obj/item/projectile/leaper/on_hit(atom/target, blocked = FALSE)
+ ..()
+ if(iscarbon(target))
+ var/mob/living/carbon/C = target
+ C.reagents.add_reagent("leaper_venom", 5)
+ return
+ if(isanimal(target))
+ var/mob/living/simple_animal/L = target
+ L.adjustHealth(25)
+
+/obj/item/projectile/leaper/on_range()
+ var/turf/T = get_turf(src)
+ ..()
+ new /obj/structure/leaper_bubble(T)
+
+/obj/effect/temp_visual/leaper_projectile_impact
+ name = "leaper bubble"
+ icon = 'icons/obj/projectiles.dmi'
+ icon_state = "leaper_bubble_pop"
+ layer = ABOVE_ALL_MOB_LAYER
+ duration = 3
+
+/obj/effect/temp_visual/leaper_projectile_impact/Initialize()
+ . = ..()
+ new /obj/effect/decal/cleanable/leaper_sludge(get_turf(src))
+
+/obj/effect/decal/cleanable/leaper_sludge
+ name = "leaper sludge"
+ desc = "A small pool of sludge, containing trace amounts of leaper venom."
+ icon = 'icons/effects/tomatodecal.dmi'
+ icon_state = "tomato_floor1"
+
+/obj/structure/leaper_bubble
+ name = "leaper bubble"
+ desc = "A floating bubble containing leaper venom. The contents are under a surprising amount of pressure."
+ icon = 'icons/obj/projectiles.dmi'
+ icon_state = "leaper"
+ max_integrity = 10
+ density = FALSE
+
+/obj/structure/leaper_bubble/Initialize()
+ . = ..()
+ float(on = TRUE)
+ QDEL_IN(src, 100)
+
+/obj/structure/leaper_bubble/Destroy()
+ new /obj/effect/temp_visual/leaper_projectile_impact(get_turf(src))
+ playsound(src,'sound/effects/snap.ogg',50, 1, -1)
+ return ..()
+
+/obj/structure/leaper_bubble/Crossed(atom/movable/AM)
+ if(isliving(AM))
+ var/mob/living/L = AM
+ if(!istype(L, /mob/living/simple_animal/hostile/jungle/leaper))
+ playsound(src,'sound/effects/snap.ogg',50, 1, -1)
+ L.Knockdown(50)
+ if(iscarbon(L))
+ var/mob/living/carbon/C = L
+ C.reagents.add_reagent("leaper_venom", 5)
+ if(isanimal(L))
+ var/mob/living/simple_animal/A = L
+ A.adjustHealth(25)
+ qdel(src)
+ return ..()
+
+/datum/reagent/toxin/leaper_venom
+ name = "Leaper venom"
+ id = "leaper_venom"
+ description = "A toxin spat out by leapers that, while harmless in small doses, quickly creates a toxic reaction if too much is in the body."
+ color = "#801E28" // rgb: 128, 30, 40
+ toxpwr = 0
+ taste_description = "french cuisine"
+ taste_mult = 1.3
+
+/datum/reagent/toxin/leaper_venom/on_mob_life(mob/living/M)
+ if(volume >= 10)
+ M.adjustToxLoss(5, 0)
+ ..()
+
+/obj/effect/temp_visual/leaper_crush
+ name = "grim tidings"
+ desc = "Incoming leaper!"
+ icon = 'icons/effects/96x96.dmi'
+ icon_state = "lily_pad"
+ layer = BELOW_MOB_LAYER
+ pixel_x = -32
+ pixel_y = -32
+ duration = 30
+
+/mob/living/simple_animal/hostile/jungle/leaper/Initialize()
+ . = ..()
+ verbs -= /mob/living/verb/pulled
+
+/mob/living/simple_animal/hostile/jungle/leaper/CtrlClickOn(atom/A)
+ face_atom(A)
+ target = A
+ if(!isturf(loc))
+ return
+ if(next_move > world.time)
+ return
+ if(hopping)
+ return
+ if(isliving(A))
+ var/mob/living/L = A
+ if(L.incapacitated())
+ BellyFlop()
+ return
+ if(hop_cooldown <= world.time)
+ Hop(player_hop = TRUE)
+
+/mob/living/simple_animal/hostile/jungle/leaper/AttackingTarget()
+ if(isliving(target))
+ return
+ return ..()
+
+/mob/living/simple_animal/hostile/jungle/leaper/handle_automated_action()
+ if(hopping || projectile_ready)
+ return
+ . = ..()
+ if(target)
+ if(isliving(target))
+ var/mob/living/L = target
+ if(L.incapacitated())
+ BellyFlop()
+ return
+ if(!hopping)
+ Hop()
+
+/mob/living/simple_animal/hostile/jungle/leaper/Life()
+ . = ..()
+ update_icons()
+
+/mob/living/simple_animal/hostile/jungle/leaper/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
+ if(prob(33) && !ckey)
+ ranged_cooldown = 0 //Keeps em on their toes instead of a constant rotation
+ ..()
+
+/mob/living/simple_animal/hostile/jungle/leaper/OpenFire()
+ face_atom(target)
+ if(ranged_cooldown <= world.time)
+ if(ckey)
+ if(hopping)
+ return
+ if(isliving(target))
+ var/mob/living/L = target
+ if(L.incapacitated())
+ return //No stunlocking. Hop on them after you stun them, you donk.
+ if(AIStatus == AI_ON && !projectile_ready && !ckey)
+ return
+ . = ..(target)
+ projectile_ready = FALSE
+ update_icons()
+
+/mob/living/simple_animal/hostile/jungle/leaper/proc/Hop(player_hop = FALSE)
+ if(z != target.z)
+ return
+ hopping = TRUE
+ density = FALSE
+ pass_flags |= PASSMOB
+ notransform = TRUE
+ var/turf/new_turf = locate((target.x + rand(-3,3)),(target.y + rand(-3,3)),target.z)
+ if(player_hop)
+ new_turf = get_turf(target)
+ hop_cooldown = world.time + PLAYER_HOP_DELAY
+ if(AIStatus == AI_ON && ranged_cooldown <= world.time)
+ projectile_ready = TRUE
+ update_icons()
+ throw_at(new_turf, max(3,get_dist(src,new_turf)), 1, src, FALSE, callback = CALLBACK(src, .FinishHop))
+
+/mob/living/simple_animal/hostile/jungle/leaper/proc/FinishHop()
+ density = TRUE
+ notransform = FALSE
+ pass_flags &= ~PASSMOB
+ hopping = FALSE
+ playsound(src.loc, 'sound/effects/meteorimpact.ogg', 100, 1)
+ if(target && AIStatus == AI_ON && projectile_ready && !ckey)
+ face_atom(target)
+ addtimer(CALLBACK(src, .proc/OpenFire, target), 5)
+
+/mob/living/simple_animal/hostile/jungle/leaper/proc/BellyFlop()
+ var/turf/new_turf = get_turf(target)
+ hopping = TRUE
+ notransform = TRUE
+ new /obj/effect/temp_visual/leaper_crush(new_turf)
+ addtimer(CALLBACK(src, .proc/BellyFlopHop, new_turf), 30)
+
+/mob/living/simple_animal/hostile/jungle/leaper/proc/BellyFlopHop(turf/T)
+ density = FALSE
+ throw_at(T, get_dist(src,T),1,src, FALSE, callback = CALLBACK(src, .proc/Crush))
+
+/mob/living/simple_animal/hostile/jungle/leaper/proc/Crush()
+ hopping = FALSE
+ density = TRUE
+ notransform = FALSE
+ playsound(src, 'sound/effects/meteorimpact.ogg', 200, 1)
+ for(var/mob/living/L in orange(1, src))
+ L.adjustBruteLoss(35)
+ if(!QDELETED(L)) // Some mobs are deleted on death
+ var/throw_dir = get_dir(src, L)
+ if(L.loc == loc)
+ throw_dir = pick(GLOB.alldirs)
+ var/throwtarget = get_edge_target_turf(src, throw_dir)
+ L.throw_at(throwtarget, 3, 1)
+ visible_message("[L] is thrown clear of [src]!")
+ if(ckey)//Lessens ability to chain stun as a player
+ ranged_cooldown = ranged_cooldown_time + world.time
+ update_icons()
+
+/mob/living/simple_animal/hostile/jungle/leaper/Goto()
+ return
+
+/mob/living/simple_animal/hostile/jungle/leaper/throw_impact()
+ return
+
+/mob/living/simple_animal/hostile/jungle/leaper/update_icons()
+ . = ..()
+ if(stat)
+ icon_state = "leaper_dead"
+ return
+ if(ranged_cooldown <= world.time)
+ if(AIStatus == AI_ON && projectile_ready || ckey)
+ icon_state = "leaper_alert"
+ return
+ icon_state = "leaper"
+
+#undef PLAYER_HOP_DELAY
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm b/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm
new file mode 100644
index 0000000000..be51502a96
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/mega_arachnid.dm
@@ -0,0 +1,65 @@
+//Large and powerful, but timid. It won't engage anything above 50 health, or anything without legcuffs.
+//It can fire fleshy snares that legcuff anyone that it hits, making them look especially tasty to the arachnid.
+/mob/living/simple_animal/hostile/jungle/mega_arachnid
+ name = "mega arachnid"
+ desc = "Though physically imposing, it prefers to ambush its prey, and it will only engage with an already crippled opponent."
+ icon = 'icons/mob/jungle/arachnid.dmi'
+ icon_state = "arachnid"
+ icon_living = "arachnid"
+ icon_dead = "arachnid_dead"
+ melee_damage_lower = 30
+ melee_damage_upper = 30
+ maxHealth = 300
+ health = 300
+ speed = 1
+ ranged = 1
+ pixel_x = -16
+ move_to_delay = 10
+ aggro_vision_range = 9
+ speak_emote = list("chitters")
+ attack_sound = 'sound/weapons/bladeslice.ogg'
+ ranged_cooldown_time = 60
+ projectiletype = /obj/item/projectile/mega_arachnid
+ projectilesound = 'sound/weapons/pierce.ogg'
+ alpha = 50
+
+/mob/living/simple_animal/hostile/jungle/mega_arachnid/Life()
+ ..()
+ if(target && ranged_cooldown > world.time && iscarbon(target))
+ var/mob/living/carbon/C = target
+ if(!C.legcuffed && C.health < 50)
+ retreat_distance = 9
+ minimum_distance = 9
+ alpha = 125
+ return
+ retreat_distance = 0
+ minimum_distance = 0
+ alpha = 255
+
+
+/mob/living/simple_animal/hostile/jungle/mega_arachnid/Aggro()
+ ..()
+ alpha = 255
+
+/mob/living/simple_animal/hostile/jungle/mega_arachnid/LoseAggro()
+ ..()
+ alpha = 50
+
+/obj/item/projectile/mega_arachnid
+ name = "flesh snare"
+ nodamage = 1
+ damage = 0
+ icon_state = "tentacle_end"
+
+/obj/item/projectile/mega_arachnid/on_hit(atom/target, blocked = FALSE)
+ if(iscarbon(target) && blocked < 100)
+ var/obj/item/restraints/legcuffs/beartrap/mega_arachnid/B = new /obj/item/restraints/legcuffs/beartrap/mega_arachnid(get_turf(target))
+ B.Crossed(target)
+ ..()
+
+/obj/item/restraints/legcuffs/beartrap/mega_arachnid
+ name = "fleshy restraints"
+ desc = "Used by mega arachnids to immobilize their prey."
+ flags_1 = DROPDEL_1
+ icon_state = "tentacle_end"
+ icon = 'icons/obj/projectiles.dmi'
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm b/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm
new file mode 100644
index 0000000000..05896c80b5
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/mook.dm
@@ -0,0 +1,224 @@
+#define MOOK_ATTACK_NEUTRAL 0
+#define MOOK_ATTACK_WARMUP 1
+#define MOOK_ATTACK_ACTIVE 2
+#define MOOK_ATTACK_RECOVERY 3
+#define ATTACK_INTERMISSION_TIME 5
+
+//Fragile but highly aggressive wanderers that pose a large threat in numbers.
+//They'll attempt to leap at their target from afar using their hatchets.
+/mob/living/simple_animal/hostile/jungle/mook
+ name = "wanderer"
+ desc = "This unhealthy looking primitive is wielding a rudimentary hatchet, swinging it with wild abandon. One isn't much of a threat, but in numbers they can quickly overwhelm a superior opponent."
+ icon = 'icons/mob/jungle/mook.dmi'
+ icon_state = "mook"
+ icon_living = "mook"
+ icon_dead = "mook_dead"
+ pixel_x = -16
+ maxHealth = 45
+ health = 45
+ melee_damage_lower = 30
+ melee_damage_upper = 30
+ pixel_y = -8
+ ranged = TRUE
+ ranged_cooldown_time = 10
+ pass_flags = LETPASSTHROW
+ robust_searching = TRUE
+ stat_attack = UNCONSCIOUS
+ attack_sound = 'sound/weapons/rapierhit.ogg'
+ death_sound = 'sound/voice/mook_death.ogg'
+ aggro_vision_range = 15 //A little more aggressive once in combat to balance out their really low HP
+ var/attack_state = MOOK_ATTACK_NEUTRAL
+ var/struck_target_leap = FALSE
+
+/mob/living/simple_animal/hostile/jungle/mook/CanPass(atom/movable/O)
+ if(istype(O, /mob/living/simple_animal/hostile/jungle/mook))
+ var/mob/living/simple_animal/hostile/jungle/mook/M = O
+ if(M.attack_state == MOOK_ATTACK_ACTIVE && M.throwing)
+ return TRUE
+ return ..()
+
+/mob/living/simple_animal/hostile/jungle/mook/death()
+ desc = "A deceased primitive. Upon closer inspection, it was suffering from severe cellular degeneration and its garments are machine made..."//Can you guess the twist
+ return ..()
+
+/mob/living/simple_animal/hostile/jungle/mook/AttackingTarget()
+ if(isliving(target))
+ if(ranged_cooldown <= world.time && attack_state == MOOK_ATTACK_NEUTRAL)
+ var/mob/living/L = target
+ if(L.incapacitated())
+ WarmupAttack(forced_slash_combo = TRUE)
+ return
+ WarmupAttack()
+ return
+ return ..()
+
+/mob/living/simple_animal/hostile/jungle/mook/Goto()
+ if(attack_state != MOOK_ATTACK_NEUTRAL)
+ return
+ return ..()
+
+/mob/living/simple_animal/hostile/jungle/mook/Move()
+ if(attack_state == MOOK_ATTACK_WARMUP || attack_state == MOOK_ATTACK_RECOVERY)
+ return
+ return ..()
+
+/mob/living/simple_animal/hostile/jungle/mook/proc/WarmupAttack(forced_slash_combo = FALSE)
+ if(attack_state == MOOK_ATTACK_NEUTRAL && target)
+ attack_state = MOOK_ATTACK_WARMUP
+ walk(src,0)
+ update_icons()
+ if(prob(50) && get_dist(src,target) <= 3 || forced_slash_combo)
+ addtimer(CALLBACK(src, .proc/SlashCombo), ATTACK_INTERMISSION_TIME)
+ return
+ addtimer(CALLBACK(src, .proc/LeapAttack), ATTACK_INTERMISSION_TIME + rand(0,3))
+ return
+ attack_state = MOOK_ATTACK_RECOVERY
+ ResetNeutral()
+
+/mob/living/simple_animal/hostile/jungle/mook/proc/SlashCombo()
+ if(attack_state == MOOK_ATTACK_WARMUP && !stat)
+ attack_state = MOOK_ATTACK_ACTIVE
+ update_icons()
+ SlashAttack()
+ addtimer(CALLBACK(src, .proc/SlashAttack), 3)
+ addtimer(CALLBACK(src, .proc/SlashAttack), 6)
+ addtimer(CALLBACK(src, .proc/AttackRecovery), 9)
+
+/mob/living/simple_animal/hostile/jungle/mook/proc/SlashAttack()
+ if(target && !stat && attack_state == MOOK_ATTACK_ACTIVE)
+ melee_damage_lower = 15
+ melee_damage_upper = 15
+ var/mob_direction = get_dir(src,target)
+ if(get_dist(src,target) > 1)
+ step(src,mob_direction)
+ if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from) && isliving(target))
+ var/mob/living/L = target
+ L.attack_animal(src)
+ return
+ var/swing_turf = get_step(src,mob_direction)
+ new /obj/effect/temp_visual/kinetic_blast(swing_turf)
+ playsound(src, 'sound/weapons/slashmiss.ogg', 50, 1)
+
+/mob/living/simple_animal/hostile/jungle/mook/proc/LeapAttack()
+ if(target && !stat && attack_state == MOOK_ATTACK_WARMUP)
+ attack_state = MOOK_ATTACK_ACTIVE
+ density = FALSE
+ melee_damage_lower = 30
+ melee_damage_upper = 30
+ update_icons()
+ new /obj/effect/temp_visual/mook_dust(get_turf(src))
+ playsound(src, 'sound/weapons/thudswoosh.ogg', 25, 1)
+ playsound(src, 'sound/voice/mook_leap_yell.ogg', 100, 1)
+ var/target_turf = get_turf(target)
+ throw_at(target_turf, 7, 1, src, FALSE, callback = CALLBACK(src, .proc/AttackRecovery))
+ return
+ attack_state = MOOK_ATTACK_RECOVERY
+ ResetNeutral()
+
+/mob/living/simple_animal/hostile/jungle/mook/proc/AttackRecovery()
+ if(attack_state == MOOK_ATTACK_ACTIVE && !stat)
+ attack_state = MOOK_ATTACK_RECOVERY
+ density = TRUE
+ face_atom(target)
+ if(!struck_target_leap)
+ update_icons()
+ struck_target_leap = FALSE
+ if(prob(40))
+ attack_state = MOOK_ATTACK_NEUTRAL
+ if(target)
+ if(isliving(target))
+ var/mob/living/L = target
+ if(L.incapacitated() && L.stat != DEAD)
+ addtimer(CALLBACK(src, .proc/WarmupAttack, TRUE), ATTACK_INTERMISSION_TIME)
+ return
+ addtimer(CALLBACK(src, .proc/WarmupAttack), ATTACK_INTERMISSION_TIME)
+ return
+ addtimer(CALLBACK(src, .proc/ResetNeutral), ATTACK_INTERMISSION_TIME)
+
+/mob/living/simple_animal/hostile/jungle/mook/proc/ResetNeutral()
+ if(attack_state == MOOK_ATTACK_RECOVERY)
+ attack_state = MOOK_ATTACK_NEUTRAL
+ ranged_cooldown = world.time + ranged_cooldown_time
+ update_icons()
+ if(target && !stat)
+ update_icons()
+ Goto(target, move_to_delay, minimum_distance)
+
+/mob/living/simple_animal/hostile/jungle/mook/throw_impact(atom/hit_atom, throwingdatum)
+ . = ..()
+ if(isliving(hit_atom) && attack_state == MOOK_ATTACK_ACTIVE)
+ var/mob/living/L = hit_atom
+ if(CanAttack(L))
+ L.attack_animal(src)
+ struck_target_leap = TRUE
+ density = TRUE
+ update_icons()
+ var/mook_under_us = FALSE
+ for(var/A in get_turf(src))
+ if(struck_target_leap && mook_under_us)
+ break
+ if(A == src)
+ continue
+ if(isliving(A))
+ var/mob/living/ML = A
+ if(!struck_target_leap && CanAttack(ML))//Check if some joker is attempting to use rest to evade us
+ struck_target_leap = TRUE
+ ML.attack_animal(src)
+ density = TRUE
+ struck_target_leap = TRUE
+ update_icons()
+ continue
+ if(istype(ML, /mob/living/simple_animal/hostile/jungle/mook) && !mook_under_us)//If we land on the same tile as another mook, spread out so we don't stack our sprite on the same tile
+ var/mob/living/simple_animal/hostile/jungle/mook/M = ML
+ if(!M.stat)
+ mook_under_us = TRUE
+ var/anydir = pick(GLOB.cardinals)
+ Move(get_step(src, anydir), anydir)
+ continue
+
+/mob/living/simple_animal/hostile/jungle/mook/handle_automated_action()
+ if(attack_state)
+ return
+ return ..()
+
+/mob/living/simple_animal/hostile/jungle/mook/OpenFire()
+ if(isliving(target))
+ var/mob/living/L = target
+ if(L.incapacitated())
+ return
+ WarmupAttack()
+
+/mob/living/simple_animal/hostile/jungle/mook/update_icons()
+ . = ..()
+ if(!stat)
+ switch(attack_state)
+ if(MOOK_ATTACK_NEUTRAL)
+ icon_state = "mook"
+ if(MOOK_ATTACK_WARMUP)
+ icon_state = "mook_warmup"
+ if(MOOK_ATTACK_ACTIVE)
+ if(!density)
+ icon_state = "mook_leap"
+ return
+ if(struck_target_leap)
+ icon_state = "mook_strike"
+ return
+ icon_state = "mook_slash_combo"
+ if(MOOK_ATTACK_RECOVERY)
+ icon_state = "mook"
+
+/obj/effect/temp_visual/mook_dust
+ name = "dust"
+ desc = "it's just a dust cloud!"
+ icon = 'icons/mob/jungle/mook.dmi'
+ icon_state = "mook_leap_cloud"
+ layer = BELOW_MOB_LAYER
+ pixel_x = -16
+ pixel_y = -16
+ duration = 10
+
+#undef MOOK_ATTACK_NEUTRAL
+#undef MOOK_ATTACK_WARMUP
+#undef MOOK_ATTACK_ACTIVE
+#undef MOOK_ATTACK_RECOVERY
+#undef ATTACK_INTERMISSION_TIME
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm b/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm
new file mode 100644
index 0000000000..3e28a789ad
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/jungle/seedling.dm
@@ -0,0 +1,244 @@
+#define SEEDLING_STATE_NEUTRAL 0
+#define SEEDLING_STATE_WARMUP 1
+#define SEEDLING_STATE_ACTIVE 2
+#define SEEDLING_STATE_RECOVERY 3
+
+//A plant rooted in the ground that forfeits its melee attack in favor of ranged barrages.
+//It will fire flurries of solar energy, and occasionally charge up a powerful blast that makes it vulnerable to attack.
+/mob/living/simple_animal/hostile/jungle/seedling
+ name = "seedling"
+ desc = "This oversized, predatory flower conceals what can only be described as an organic energy cannon, and it will not die until its hidden vital organs are sliced out. \
+ The concentrated streams of energy it sometimes produces require its full attention, attacking it during this time will prevent it from finishing its attack."
+ icon = 'icons/mob/jungle/seedling.dmi'
+ icon_state = "seedling"
+ icon_living = "seedling"
+ icon_dead = "seedling_dead"
+ maxHealth = 100
+ health = 100
+ melee_damage_lower = 30
+ melee_damage_upper = 30
+ pixel_x = -16
+ pixel_y = -14
+ minimum_distance = 3
+ move_to_delay = 20
+ vision_range = 9
+ aggro_vision_range = 15
+ ranged = TRUE
+ ranged_cooldown_time = 10
+ projectiletype = /obj/item/projectile/seedling
+ projectilesound = 'sound/weapons/pierce.ogg'
+ robust_searching = TRUE
+ stat_attack = UNCONSCIOUS
+ anchored = TRUE
+ var/combatant_state = SEEDLING_STATE_NEUTRAL
+ var/obj/seedling_weakpoint/weak_point
+ var/mob/living/beam_debuff_target
+ var/solar_beam_identifier = 0
+
+/obj/item/projectile/seedling
+ name = "solar energy"
+ icon_state = "seedling"
+ damage = 10
+ damage_type = BURN
+ light_range = 2
+ flag = "energy"
+ light_color = LIGHT_COLOR_YELLOW
+ hitsound = 'sound/weapons/sear.ogg'
+ hitsound_wall = 'sound/weapons/effects/searwall.ogg'
+ nondirectional_sprite = TRUE
+
+/obj/item/projectile/seedling/Collide(atom/A)//Stops seedlings from destroying other jungle mobs through FF
+ if(isliving(A))
+ var/mob/living/L = A
+ if("jungle" in L.faction)
+ return FALSE
+ return ..()
+
+/obj/effect/temp_visual/solarbeam_killsat
+ name = "beam of solar energy"
+ icon_state = "solar_beam"
+ icon = 'icons/effects/beam.dmi'
+ layer = LIGHTING_LAYER
+ duration = 5
+ randomdir = FALSE
+
+/datum/status_effect/seedling_beam_indicator
+ id = "seedling beam indicator"
+ duration = 30
+ status_type = STATUS_EFFECT_MULTIPLE
+ alert_type = null
+ tick_interval = 1
+ var/obj/screen/seedling/seedling_screen_object
+ var/atom/target
+
+
+/datum/status_effect/seedling_beam_indicator/on_creation(mob/living/new_owner, target_plant)
+ . = ..()
+ if(.)
+ target = target_plant
+ tick()
+
+/datum/status_effect/seedling_beam_indicator/on_apply()
+ if(owner.client)
+ seedling_screen_object = new /obj/screen/seedling()
+ owner.client.screen += seedling_screen_object
+ tick()
+ return ..()
+
+/datum/status_effect/seedling_beam_indicator/Destroy()
+ if(owner)
+ if(owner.client)
+ owner.client.screen -= seedling_screen_object
+ return ..()
+
+/datum/status_effect/seedling_beam_indicator/tick()
+ var/target_angle = Get_Angle(owner, target)
+ var/matrix/final = matrix()
+ final.Turn(target_angle)
+ seedling_screen_object.transform = final
+
+/obj/screen/seedling
+ icon = 'icons/mob/jungle/arachnid.dmi'
+ icon_state = "seedling_beam_indicator"
+ screen_loc = "CENTER:-16,CENTER:-16"
+
+/mob/living/simple_animal/hostile/jungle/seedling/Goto()
+ if(combatant_state != SEEDLING_STATE_NEUTRAL)
+ return
+ return ..()
+
+/mob/living/simple_animal/hostile/jungle/seedling/AttackingTarget()
+ if(isliving(target))
+ if(ranged_cooldown <= world.time && combatant_state == SEEDLING_STATE_NEUTRAL)
+ OpenFire(target)
+ return
+ return ..()
+
+/mob/living/simple_animal/hostile/jungle/seedling/OpenFire()
+ WarmupAttack()
+
+/mob/living/simple_animal/hostile/jungle/seedling/proc/WarmupAttack()
+ if(combatant_state == SEEDLING_STATE_NEUTRAL)
+ combatant_state = SEEDLING_STATE_WARMUP
+ walk(src,0)
+ update_icons()
+ var/target_dist = get_dist(src,target)
+ var/living_target_check = isliving(target)
+ if(living_target_check)
+ if(target_dist > 7)//Offscreen check
+ SolarBeamStartup(target)
+ return
+ if(get_dist(src,target) >= 4 && prob(40))
+ SolarBeamStartup(target)
+ return
+ addtimer(CALLBACK(src, .proc/Volley), 5)
+
+/mob/living/simple_animal/hostile/jungle/seedling/proc/SolarBeamStartup(mob/living/living_target)//It's more like requiem than final spark
+ if(combatant_state == SEEDLING_STATE_WARMUP && target)
+ combatant_state = SEEDLING_STATE_ACTIVE
+ living_target.apply_status_effect(/datum/status_effect/seedling_beam_indicator, src)
+ beam_debuff_target = living_target
+ playsound(src,'sound/effects/seedling_chargeup.ogg', 100, 0)
+ if(get_dist(src,living_target) > 7)
+ playsound(living_target,'sound/effects/seedling_chargeup.ogg', 100, 0)
+ solar_beam_identifier = world.time
+ addtimer(CALLBACK(src, .proc/Beamu, living_target, solar_beam_identifier), 35)
+
+/mob/living/simple_animal/hostile/jungle/seedling/proc/Beamu(mob/living/living_target, beam_id = 0)
+ if(combatant_state == SEEDLING_STATE_ACTIVE && living_target && beam_id == solar_beam_identifier)
+ if(living_target.z == z)
+ update_icons()
+ var/obj/effect/temp_visual/solarbeam_killsat/S = new (get_turf(src))
+ var/matrix/starting = matrix()
+ starting.Scale(1,32)
+ starting.Translate(0,520)
+ S.transform = starting
+ var/obj/effect/temp_visual/solarbeam_killsat/K = new (get_turf(living_target))
+ var/matrix/final = matrix()
+ final.Scale(1,32)
+ final.Translate(0,512)
+ K.transform = final
+ living_target.adjustFireLoss(30)
+ living_target.adjust_fire_stacks(0.2)//Just here for the showmanship
+ living_target.IgniteMob()
+ playsound(living_target,'sound/weapons/sear.ogg', 50, 1)
+ addtimer(CALLBACK(src, .proc/AttackRecovery), 5)
+ return
+ AttackRecovery()
+
+/mob/living/simple_animal/hostile/jungle/seedling/proc/Volley()
+ if(combatant_state == SEEDLING_STATE_WARMUP && target)
+ combatant_state = SEEDLING_STATE_ACTIVE
+ update_icons()
+ var/datum/callback/cb = CALLBACK(src, .proc/InaccurateShot)
+ for(var/i in 1 to 13)
+ addtimer(cb, i)
+ addtimer(CALLBACK(src, .proc/AttackRecovery), 14)
+
+/mob/living/simple_animal/hostile/jungle/seedling/proc/InaccurateShot()
+ if(!QDELETED(target) && combatant_state == SEEDLING_STATE_ACTIVE && !stat)
+ if(get_dist(src,target) <= 3)//If they're close enough just aim straight at them so we don't miss at point blank ranges
+ Shoot(target)
+ return
+ var/turf/our_turf = get_turf(src)
+ var/obj/item/projectile/seedling/readied_shot = new /obj/item/projectile/seedling(our_turf)
+ readied_shot.current = our_turf
+ readied_shot.starting = our_turf
+ readied_shot.firer = src
+ readied_shot.original = target
+ readied_shot.yo = target.y - our_turf.y + rand(-1,1)
+ readied_shot.xo = target.x - our_turf.x + rand(-1,1)
+ readied_shot.fire()
+ playsound(src, projectilesound, 100, 1)
+
+/mob/living/simple_animal/hostile/jungle/seedling/proc/AttackRecovery()
+ if(combatant_state == SEEDLING_STATE_ACTIVE)
+ combatant_state = SEEDLING_STATE_RECOVERY
+ update_icons()
+ ranged_cooldown = world.time + ranged_cooldown_time
+ if(target)
+ face_atom(target)
+ addtimer(CALLBACK(src, .proc/ResetNeutral), 10)
+
+/mob/living/simple_animal/hostile/jungle/seedling/proc/ResetNeutral()
+ combatant_state = SEEDLING_STATE_NEUTRAL
+ if(target && !stat)
+ update_icons()
+ Goto(target, move_to_delay, minimum_distance)
+
+/mob/living/simple_animal/hostile/jungle/seedling/adjustHealth()
+ . = ..()
+ if(combatant_state == SEEDLING_STATE_ACTIVE && beam_debuff_target)
+ beam_debuff_target.remove_status_effect(/datum/status_effect/seedling_beam_indicator)
+ beam_debuff_target = null
+ solar_beam_identifier = 0
+ AttackRecovery()
+
+/mob/living/simple_animal/hostile/jungle/seedling/update_icons()
+ . = ..()
+ if(!stat)
+ switch(combatant_state)
+ if(SEEDLING_STATE_NEUTRAL)
+ icon_state = "seedling"
+ if(SEEDLING_STATE_WARMUP)
+ icon_state = "seedling_charging"
+ if(SEEDLING_STATE_ACTIVE)
+ icon_state = "seedling_fire"
+ if(SEEDLING_STATE_RECOVERY)
+ icon_state = "seedling"
+
+/mob/living/simple_animal/hostile/jungle/seedling/GiveTarget()
+ if(target)
+ if(combatant_state == SEEDLING_STATE_WARMUP || combatant_state == SEEDLING_STATE_ACTIVE)//So it doesn't 180 and blast you in the face while it's firing at someone else
+ return
+ return ..()
+
+/mob/living/simple_animal/hostile/jungle/seedling/LoseTarget()
+ if(combatant_state == SEEDLING_STATE_WARMUP || combatant_state == SEEDLING_STATE_ACTIVE)
+ return
+ return ..()
+
+#undef SEEDLING_STATE_NEUTRAL
+#undef SEEDLING_STATE_WARMUP
+#undef SEEDLING_STATE_ACTIVE
+#undef SEEDLING_STATE_RECOVERY
diff --git a/code/modules/mob/living/simple_animal/hostile/jungle_mobs.dm b/code/modules/mob/living/simple_animal/hostile/jungle_mobs.dm
deleted file mode 100644
index b3e1479b91..0000000000
--- a/code/modules/mob/living/simple_animal/hostile/jungle_mobs.dm
+++ /dev/null
@@ -1,833 +0,0 @@
-/mob/living/simple_animal/hostile/jungle
- vision_range = 5
- atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
- faction = list("jungle")
- weather_immunities = list("acid")
- obj_damage = 30
- environment_smash = ENVIRONMENT_SMASH_WALLS
- minbodytemp = 0
- maxbodytemp = 450
- response_help = "pokes"
- response_disarm = "shoves"
- response_harm = "strikes"
- status_flags = 0
- a_intent = INTENT_HARM
- see_in_dark = 4
- lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
- mob_size = MOB_SIZE_LARGE
-
-
-
-//Mega arachnid
-
-/mob/living/simple_animal/hostile/jungle/mega_arachnid
- name = "mega arachnid"
- desc = "Though physically imposing, it prefers to ambush its prey, and it will only engage with an already crippled opponent."
- melee_damage_lower = 30
- melee_damage_upper = 30
- maxHealth = 300
- health = 300
- speed = 1
- ranged = 1
- pixel_x = -16
- move_to_delay = 10
- aggro_vision_range = 9
- speak_emote = list("chitters")
- attack_sound = 'sound/weapons/bladeslice.ogg'
- ranged_cooldown_time = 60
- projectiletype = /obj/item/projectile/mega_arachnid
- projectilesound = 'sound/weapons/pierce.ogg'
- icon = 'icons/mob/jungle/arachnid.dmi'
- icon_state = "arachnid"
- icon_living = "arachnid"
- icon_dead = "dead_purple"
- alpha = 50
-
-/mob/living/simple_animal/hostile/jungle/mega_arachnid/Life()
- ..()
- if(target && ranged_cooldown > world.time && iscarbon(target))
- var/mob/living/carbon/C = target
- if(!C.legcuffed && C.health < 50)
- retreat_distance = 9
- minimum_distance = 9
- alpha = 125
- return
- retreat_distance = 0
- minimum_distance = 0
- alpha = 255
-
-
-/mob/living/simple_animal/hostile/jungle/mega_arachnid/Aggro()
- ..()
- alpha = 255
-
-/mob/living/simple_animal/hostile/jungle/mega_arachnid/LoseAggro()
- ..()
- alpha = 50
-
-/obj/item/projectile/mega_arachnid
- name = "flesh snare"
- nodamage = 1
- damage = 0
- icon_state = "tentacle_end"
-
-/obj/item/projectile/mega_arachnid/on_hit(atom/target, blocked = FALSE)
- if(iscarbon(target) && blocked < 100)
- var/obj/item/restraints/legcuffs/beartrap/mega_arachnid/B = new /obj/item/restraints/legcuffs/beartrap/mega_arachnid(get_turf(target))
- B.Crossed(target)
- ..()
-
-/obj/item/restraints/legcuffs/beartrap/mega_arachnid
- name = "fleshy restraints"
- desc = "Used by mega arachnids to immobilize their prey."
- flags_1 = DROPDEL_1
- icon_state = "tentacle_end"
- icon = 'icons/obj/projectiles.dmi'
-
-////Leaper////
-
-#define PLAYER_HOP_DELAY 25
-
-/mob/living/simple_animal/hostile/jungle/leaper
- atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
- faction = list("jungle")
- weather_immunities = list("acid")
- obj_damage = 30
- environment_smash = ENVIRONMENT_SMASH_WALLS
- maxHealth = 300
- health = 300
- minbodytemp = 0
- maxbodytemp = 450
- response_help = "pokes"
- response_disarm = "shoves"
- response_harm = "strikes"
- status_flags = 0
- a_intent = INTENT_HARM
- see_in_dark = 4
- ranged = TRUE
- projectiletype = /obj/item/projectile/leaper
- projectilesound = 'sound/weapons/pierce.ogg'
- ranged_cooldown_time = 30
- pixel_x = -16
- icon = 'icons/mob/jungle/arachnid.dmi'
- icon_state = "leaper"
- icon_living = "leaper"
- icon_dead = "leaper_dead"
- layer = LARGE_MOB_LAYER
- name = "leaper"
- desc = "Commonly referred to as 'leapers', the Geron Toad is a massive beast that spits out highly pressurized bubbles containing a unique toxin, knocking down its prey and then crushing it with its girth."
- speed = 10
- stat_attack = 1
- robust_searching = 1
- var/hopping = FALSE
- var/hop_cooldown = 0 //Strictly for player controlled leapers
- var/projectile_ready = FALSE //Stopping AI leapers from firing whenever they want, and only doing it after a hop has finished instead
-
-/obj/item/projectile/leaper
- name = "leaper bubble"
- icon_state = "leaper"
- knockdown = 50
- damage = 0
- range = 7
- hitsound = 'sound/effects/snap.ogg'
- nondirectional_sprite = TRUE
- impact_effect_type = /obj/effect/temp_visual/leaper_projectile_impact
-
-/obj/item/projectile/leaper/on_hit(atom/target, blocked = FALSE)
- ..()
- if(iscarbon(target))
- var/mob/living/carbon/C = target
- C.reagents.add_reagent("leaper_venom", 5)
- return
- if(isanimal(target))
- var/mob/living/simple_animal/L = target
- L.adjustHealth(25)
-
-/obj/item/projectile/leaper/on_range()
- var/turf/T = get_turf(src)
- ..()
- new /obj/structure/leaper_bubble(T)
-
-/obj/effect/temp_visual/leaper_projectile_impact
- name = "leaper bubble"
- icon = 'icons/obj/projectiles.dmi'
- icon_state = "leaper_bubble_pop"
- layer = ABOVE_ALL_MOB_LAYER
- duration = 3
-
-/obj/effect/temp_visual/leaper_projectile_impact/Initialize()
- . = ..()
- new /obj/effect/decal/cleanable/leaper_sludge(get_turf(src))
-
-/obj/effect/decal/cleanable/leaper_sludge
- name = "leaper sludge"
- desc = "A small pool of sludge, containing trace amounts of leaper venom"
- icon = 'icons/effects/tomatodecal.dmi'
- icon_state = "tomato_floor1"
-
-/obj/structure/leaper_bubble
- name = "leaper bubble"
- desc = "A floating bubble containing leaper venom, the contents are under a surprising amount of pressure."
- icon = 'icons/obj/projectiles.dmi'
- icon_state = "leaper"
- max_integrity = 10
- density = FALSE
-
-/obj/structure/leaper_bubble/Initialize()
- . = ..()
- float(on = TRUE)
- QDEL_IN(src, 100)
-
-/obj/structure/leaper_bubble/Destroy()
- new /obj/effect/temp_visual/leaper_projectile_impact(get_turf(src))
- playsound(src,'sound/effects/snap.ogg',50, 1, -1)
- return ..()
-
-/obj/structure/leaper_bubble/Crossed(atom/movable/AM)
- if(isliving(AM))
- var/mob/living/L = AM
- if(!istype(L, /mob/living/simple_animal/hostile/jungle/leaper))
- playsound(src,'sound/effects/snap.ogg',50, 1, -1)
- L.Knockdown(50)
- if(iscarbon(L))
- var/mob/living/carbon/C = L
- C.reagents.add_reagent("leaper_venom", 5)
- if(isanimal(L))
- var/mob/living/simple_animal/A = L
- A.adjustHealth(25)
- qdel(src)
- return ..()
-
-/datum/reagent/toxin/leaper_venom
- name = "Leaper venom"
- id = "leaper_venom"
- description = "A toxin spat out by leapers that while harmless in small doses, quickly creates a toxic reaction if too much is in the body."
- color = "#801E28" // rgb: 128, 30, 40
- toxpwr = 0
- taste_description = "french cuisine"
- taste_mult = 1.3
-
-/datum/reagent/toxin/leaper_venom/on_mob_life(mob/living/M)
- if(volume >= 10)
- M.adjustToxLoss(5, 0)
- ..()
-
-/obj/effect/temp_visual/leaper_crush
- name = "Grim tidings"
- desc = "Incoming leaper!"
- icon = 'icons/effects/96x96.dmi'
- icon_state = "lily_pad"
- layer = BELOW_MOB_LAYER
- pixel_x = -32
- pixel_y = -32
- duration = 30
-
-/mob/living/simple_animal/hostile/jungle/leaper/Initialize()
- . = ..()
- verbs -= /mob/living/verb/pulled
-
-/mob/living/simple_animal/hostile/jungle/leaper/CtrlClickOn(atom/A)
- face_atom(A)
- target = A
- if(!isturf(loc))
- return
- if(next_move > world.time)
- return
- if(hopping)
- return
- if(isliving(A))
- var/mob/living/L = A
- if(L.incapacitated())
- BellyFlop()
- return
- if(hop_cooldown <= world.time)
- Hop(player_hop = TRUE)
-
-/mob/living/simple_animal/hostile/jungle/leaper/AttackingTarget()
- if(isliving(target))
- return
- return ..()
-
-/mob/living/simple_animal/hostile/jungle/leaper/handle_automated_action()
- if(hopping || projectile_ready)
- return
- . = ..()
- if(target)
- if(isliving(target))
- var/mob/living/L = target
- if(L.incapacitated())
- BellyFlop()
- return
- if(!hopping)
- Hop()
-
-/mob/living/simple_animal/hostile/jungle/leaper/Life()
- . = ..()
- update_icons()
-
-/mob/living/simple_animal/hostile/jungle/leaper/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
- if(prob(33) && !ckey)
- ranged_cooldown = 0 //Keeps em on their toes instead of a constant rotation
- ..()
-
-/mob/living/simple_animal/hostile/jungle/leaper/OpenFire()
- face_atom(target)
- if(ranged_cooldown <= world.time)
- if(ckey)
- if(hopping)
- return
- if(isliving(target))
- var/mob/living/L = target
- if(L.incapacitated())
- return //No stunlocking. Hop on them after you stun them, you donk.
- if(AIStatus == AI_ON && !projectile_ready && !ckey)
- return
- . = ..(target)
- projectile_ready = FALSE
- update_icons()
-
-/mob/living/simple_animal/hostile/jungle/leaper/proc/Hop(player_hop = FALSE)
- if(z != target.z)
- return
- hopping = TRUE
- density = FALSE
- pass_flags |= PASSMOB
- notransform = TRUE
- var/turf/new_turf = locate((target.x + rand(-3,3)),(target.y + rand(-3,3)),target.z)
- if(player_hop)
- new_turf = get_turf(target)
- hop_cooldown = world.time + PLAYER_HOP_DELAY
- if(AIStatus == AI_ON && ranged_cooldown <= world.time)
- projectile_ready = TRUE
- update_icons()
- throw_at(new_turf, max(3,get_dist(src,new_turf)), 1, src, FALSE, callback = CALLBACK(src, .FinishHop))
-
-/mob/living/simple_animal/hostile/jungle/leaper/proc/FinishHop()
- density = TRUE
- notransform = FALSE
- pass_flags &= ~PASSMOB
- hopping = FALSE
- playsound(src.loc, 'sound/effects/meteorimpact.ogg', 100, 1)
- if(target && AIStatus == AI_ON && projectile_ready && !ckey)
- face_atom(target)
- addtimer(CALLBACK(src, .proc/OpenFire, target), 5)
-
-/mob/living/simple_animal/hostile/jungle/leaper/proc/BellyFlop()
- var/turf/new_turf = get_turf(target)
- hopping = TRUE
- notransform = TRUE
- new /obj/effect/temp_visual/leaper_crush(new_turf)
- addtimer(CALLBACK(src, .proc/BellyFlopHop, new_turf), 30)
-
-/mob/living/simple_animal/hostile/jungle/leaper/proc/BellyFlopHop(turf/T)
- density = FALSE
- throw_at(T, get_dist(src,T),1,src, FALSE, callback = CALLBACK(src, .proc/Crush))
-
-/mob/living/simple_animal/hostile/jungle/leaper/proc/Crush()
- hopping = FALSE
- density = TRUE
- notransform = FALSE
- playsound(src, 'sound/effects/meteorimpact.ogg', 200, 1)
- for(var/mob/living/L in orange(1, src))
- L.adjustBruteLoss(35)
- if(!QDELETED(L)) // Some mobs are deleted on death
- var/throw_dir = get_dir(src, L)
- if(L.loc == loc)
- throw_dir = pick(GLOB.alldirs)
- var/throwtarget = get_edge_target_turf(src, throw_dir)
- L.throw_at(throwtarget, 3, 1)
- visible_message("[L] is thrown clear of [src]!")
- if(ckey)//Lessens ability to chain stun as a player
- ranged_cooldown = ranged_cooldown_time + world.time
- update_icons()
-
-/mob/living/simple_animal/hostile/jungle/leaper/Goto()
- return
-
-/mob/living/simple_animal/hostile/jungle/leaper/throw_impact()
- return
-
-/mob/living/simple_animal/hostile/jungle/leaper/update_icons()
- . = ..()
- if(stat)
- icon_state = "leaper_dead"
- return
- if(ranged_cooldown <= world.time)
- if(AIStatus == AI_ON && projectile_ready || ckey)
- icon_state = "leaper_alert"
- return
- icon_state = "leaper"
-
-#undef PLAYER_HOP_DELAY
-
-////JUNGLE MOOK////
-
-#define MOOK_ATTACK_NEUTRAL 0
-#define MOOK_ATTACK_WARMUP 1
-#define MOOK_ATTACK_ACTIVE 2
-#define MOOK_ATTACK_RECOVERY 3
-
-#define ATTACK_INTERMISSION_TIME 5
-
-/mob/living/simple_animal/hostile/jungle/mook
- name = "wanderer"
- desc = "This unhealthy looking primitive is wielding a rudimentary hatchet, swinging it with wild abandon. One isn't much of a threat, but in numbers they can quickly overwhelm a superior opponent."
- maxHealth = 45
- health = 45
- melee_damage_lower = 30
- melee_damage_upper = 30
- icon = 'icons/mob/jungle/arachnid.dmi'
- icon_state = "mook"
- icon_living = "mook"
- icon_dead = "mook_dead"
- pixel_x = -16
- pixel_y = -8
- ranged = TRUE
- ranged_cooldown_time = 10
- pass_flags = LETPASSTHROW
- robust_searching = TRUE
- stat_attack = UNCONSCIOUS
- attack_sound = 'sound/weapons/rapierhit.ogg'
- death_sound = 'sound/voice/mook_death.ogg'
- aggro_vision_range = 15 //A little more aggressive once in combat to balance out their really low HP
- var/attack_state = MOOK_ATTACK_NEUTRAL
- var/struck_target_leap = FALSE
-
-/mob/living/simple_animal/hostile/jungle/mook/CanPass(atom/movable/O)
- if(istype(O, /mob/living/simple_animal/hostile/jungle/mook))
- var/mob/living/simple_animal/hostile/jungle/mook/M = O
- if(M.attack_state == MOOK_ATTACK_ACTIVE && M.throwing)
- return TRUE
- return ..()
-
-/mob/living/simple_animal/hostile/jungle/mook/death()
- desc = "A deceased primitive. Upon closer inspection, it was suffering from severe cellular degeneration and its garments are machine made..."//Can you guess the twist
- return ..()
-
-/mob/living/simple_animal/hostile/jungle/mook/AttackingTarget()
- if(isliving(target))
- if(ranged_cooldown <= world.time && attack_state == MOOK_ATTACK_NEUTRAL)
- var/mob/living/L = target
- if(L.incapacitated())
- WarmupAttack(forced_slash_combo = TRUE)
- return
- WarmupAttack()
- return
- return ..()
-
-/mob/living/simple_animal/hostile/jungle/mook/Goto()
- if(attack_state != MOOK_ATTACK_NEUTRAL)
- return
- return ..()
-
-/mob/living/simple_animal/hostile/jungle/mook/Move()
- if(attack_state == MOOK_ATTACK_WARMUP || attack_state == MOOK_ATTACK_RECOVERY)
- return
- return ..()
-
-/mob/living/simple_animal/hostile/jungle/mook/proc/WarmupAttack(forced_slash_combo = FALSE)
- if(attack_state == MOOK_ATTACK_NEUTRAL && target)
- attack_state = MOOK_ATTACK_WARMUP
- walk(src,0)
- update_icons()
- if(prob(50) && get_dist(src,target) <= 3 || forced_slash_combo)
- addtimer(CALLBACK(src, .proc/SlashCombo), ATTACK_INTERMISSION_TIME)
- return
- addtimer(CALLBACK(src, .proc/LeapAttack), ATTACK_INTERMISSION_TIME + rand(0,3))
- return
- attack_state = MOOK_ATTACK_RECOVERY
- ResetNeutral()
-
-/mob/living/simple_animal/hostile/jungle/mook/proc/SlashCombo()
- if(attack_state == MOOK_ATTACK_WARMUP && !stat)
- attack_state = MOOK_ATTACK_ACTIVE
- update_icons()
- SlashAttack()
- addtimer(CALLBACK(src, .proc/SlashAttack), 3)
- addtimer(CALLBACK(src, .proc/SlashAttack), 6)
- addtimer(CALLBACK(src, .proc/AttackRecovery), 9)
-
-/mob/living/simple_animal/hostile/jungle/mook/proc/SlashAttack()
- if(target && !stat && attack_state == MOOK_ATTACK_ACTIVE)
- melee_damage_lower = 15
- melee_damage_upper = 15
- var/mob_direction = get_dir(src,target)
- if(get_dist(src,target) > 1)
- step(src,mob_direction)
- if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from) && isliving(target))
- var/mob/living/L = target
- L.attack_animal(src)
- return
- var/swing_turf = get_step(src,mob_direction)
- new /obj/effect/temp_visual/kinetic_blast(swing_turf)
- playsound(src, 'sound/weapons/slashmiss.ogg', 50, 1)
-
-/mob/living/simple_animal/hostile/jungle/mook/proc/LeapAttack()
- if(target && !stat && attack_state == MOOK_ATTACK_WARMUP)
- attack_state = MOOK_ATTACK_ACTIVE
- density = FALSE
- melee_damage_lower = 30
- melee_damage_upper = 30
- update_icons()
- new /obj/effect/temp_visual/mook_dust(get_turf(src))
- playsound(src, 'sound/weapons/thudswoosh.ogg', 25, 1)
- playsound(src, 'sound/voice/mook_leap_yell.ogg', 100, 1)
- var/target_turf = get_turf(target)
- throw_at(target_turf, 7, 1, src, FALSE, callback = CALLBACK(src, .proc/AttackRecovery))
- return
- attack_state = MOOK_ATTACK_RECOVERY
- ResetNeutral()
-
-/mob/living/simple_animal/hostile/jungle/mook/proc/AttackRecovery()
- if(attack_state == MOOK_ATTACK_ACTIVE && !stat)
- attack_state = MOOK_ATTACK_RECOVERY
- density = TRUE
- face_atom(target)
- if(!struck_target_leap)
- update_icons()
- struck_target_leap = FALSE
- if(prob(40))
- attack_state = MOOK_ATTACK_NEUTRAL
- if(target)
- if(isliving(target))
- var/mob/living/L = target
- if(L.incapacitated() && L.stat != DEAD)
- addtimer(CALLBACK(src, .proc/WarmupAttack, TRUE), ATTACK_INTERMISSION_TIME)
- return
- addtimer(CALLBACK(src, .proc/WarmupAttack), ATTACK_INTERMISSION_TIME)
- return
- addtimer(CALLBACK(src, .proc/ResetNeutral), ATTACK_INTERMISSION_TIME)
-
-/mob/living/simple_animal/hostile/jungle/mook/proc/ResetNeutral()
- if(attack_state == MOOK_ATTACK_RECOVERY)
- attack_state = MOOK_ATTACK_NEUTRAL
- ranged_cooldown = world.time + ranged_cooldown_time
- update_icons()
- if(target && !stat)
- update_icons()
- Goto(target, move_to_delay, minimum_distance)
-
-/mob/living/simple_animal/hostile/jungle/mook/throw_impact(atom/hit_atom, throwingdatum)
- . = ..()
- if(isliving(hit_atom) && attack_state == MOOK_ATTACK_ACTIVE)
- var/mob/living/L = hit_atom
- if(CanAttack(L))
- L.attack_animal(src)
- struck_target_leap = TRUE
- density = TRUE
- update_icons()
- var/mook_under_us = FALSE
- for(var/A in get_turf(src))
- if(struck_target_leap && mook_under_us)
- break
- if(A == src)
- continue
- if(isliving(A))
- var/mob/living/ML = A
- if(!struck_target_leap && CanAttack(ML))//Check if some joker is attempting to use rest to evade us
- struck_target_leap = TRUE
- ML.attack_animal(src)
- density = TRUE
- struck_target_leap = TRUE
- update_icons()
- continue
- if(istype(ML, /mob/living/simple_animal/hostile/jungle/mook) && !mook_under_us)//If we land on the same tile as another mook, spread out so we don't stack our sprite on the same tile
- var/mob/living/simple_animal/hostile/jungle/mook/M = ML
- if(!M.stat)
- mook_under_us = TRUE
- var/anydir = pick(GLOB.cardinals)
- Move(get_step(src, anydir), anydir)
- continue
-
-/mob/living/simple_animal/hostile/jungle/mook/handle_automated_action()
- if(attack_state)
- return
- return ..()
-
-/mob/living/simple_animal/hostile/jungle/mook/OpenFire()
- if(isliving(target))
- var/mob/living/L = target
- if(L.incapacitated())
- return
- WarmupAttack()
-
-/mob/living/simple_animal/hostile/jungle/mook/update_icons()
- . = ..()
- if(!stat)
- switch(attack_state)
- if(MOOK_ATTACK_NEUTRAL)
- icon_state = "mook"
- if(MOOK_ATTACK_WARMUP)
- icon_state = "mook_warmup"
- if(MOOK_ATTACK_ACTIVE)
- if(!density)
- icon_state = "mook_leap"
- return
- if(struck_target_leap)
- icon_state = "mook_strike"
- return
- icon_state = "mook_slash_combo"
- if(MOOK_ATTACK_RECOVERY)
- icon_state = "mook"
-
-/obj/effect/temp_visual/mook_dust
- name = "dust"
- desc = "it's just a dust cloud!"
- icon = 'icons/mob/jungle/arachnid.dmi'
- icon_state = "mook_leap_cloud"
- layer = BELOW_MOB_LAYER
- pixel_x = -16
- pixel_y = -16
- duration = 10
-
-#undef MOOK_ATTACK_NEUTRAL
-#undef MOOK_ATTACK_WARMUP
-#undef MOOK_ATTACK_ACTIVE
-#undef MOOK_ATTACK_RECOVERY
-#undef ATTACK_INTERMISSION_TIME
-
-////Jungle Seedling////
-
-#define SEEDLING_STATE_NEUTRAL 0
-#define SEEDLING_STATE_WARMUP 1
-#define SEEDLING_STATE_ACTIVE 2
-#define SEEDLING_STATE_RECOVERY 3
-
-
-/mob/living/simple_animal/hostile/jungle/seedling
- name = "seedling"
- desc = "This oversized, predatory flower conceals what can only be described as an organic energy cannon, and it will not die until its hidden vital organs are sliced out. \
- The concentrated streams of energy it sometimes produces require its full attention, attacking it during this time will prevent it from finishing its attack."
- maxHealth = 100
- health = 100
- melee_damage_lower = 30
- melee_damage_upper = 30
- icon = 'icons/mob/jungle/arachnid.dmi'
- icon_state = "seedling"
- icon_living = "seedling"
- icon_dead = "seedling_dead"
- pixel_x = -16
- pixel_y = -14
- minimum_distance = 3
- move_to_delay = 20
- vision_range = 9
- aggro_vision_range = 15
- ranged = TRUE
- ranged_cooldown_time = 10
- projectiletype = /obj/item/projectile/seedling
- projectilesound = 'sound/weapons/pierce.ogg'
- robust_searching = TRUE
- stat_attack = UNCONSCIOUS
- anchored = TRUE
- var/combatant_state = SEEDLING_STATE_NEUTRAL
- var/obj/seedling_weakpoint/weak_point
- var/mob/living/beam_debuff_target
- var/solar_beam_identifier = 0
-
-/obj/item/projectile/seedling
- name = "solar energy"
- icon_state = "seedling"
- damage = 10
- damage_type = BURN
- light_range = 2
- flag = "energy"
- light_color = LIGHT_COLOR_YELLOW
- hitsound = 'sound/weapons/sear.ogg'
- hitsound_wall = 'sound/weapons/effects/searwall.ogg'
- nondirectional_sprite = TRUE
-
-/obj/item/projectile/seedling/Collide(atom/A)//Stops seedlings from destroying other jungle mobs through FF
- if(isliving(A))
- var/mob/living/L = A
- if("jungle" in L.faction)
- return FALSE
- return ..()
-
-/obj/effect/temp_visual/solarbeam_killsat
- name = "beam of solar energy"
- icon_state = "solar_beam"
- icon = 'icons/effects/beam.dmi'
- layer = LIGHTING_LAYER
- duration = 5
- randomdir = FALSE
-
-/datum/status_effect/seedling_beam_indicator
- id = "seedling beam indicator"
- duration = 30
- status_type = STATUS_EFFECT_MULTIPLE
- alert_type = null
- tick_interval = 1
- var/obj/screen/seedling/seedling_screen_object
- var/atom/target
-
-
-/datum/status_effect/seedling_beam_indicator/on_creation(mob/living/new_owner, target_plant)
- . = ..()
- if(.)
- target = target_plant
- tick()
-
-/datum/status_effect/seedling_beam_indicator/on_apply()
- if(owner.client)
- seedling_screen_object = new /obj/screen/seedling()
- owner.client.screen += seedling_screen_object
- tick()
- return ..()
-
-/datum/status_effect/seedling_beam_indicator/Destroy()
- if(owner)
- if(owner.client)
- owner.client.screen -= seedling_screen_object
- return ..()
-
-/datum/status_effect/seedling_beam_indicator/tick()
- var/target_angle = Get_Angle(owner, target)
- var/matrix/final = matrix()
- final.Turn(target_angle)
- seedling_screen_object.transform = final
-
-/obj/screen/seedling
- icon = 'icons/mob/jungle/arachnid.dmi'
- icon_state = "seedling_beam_indicator"
- screen_loc = "CENTER:-16,CENTER:-16"
-
-/mob/living/simple_animal/hostile/jungle/seedling/Goto()
- if(combatant_state != SEEDLING_STATE_NEUTRAL)
- return
- return ..()
-
-/mob/living/simple_animal/hostile/jungle/seedling/AttackingTarget()
- if(isliving(target))
- if(ranged_cooldown <= world.time && combatant_state == SEEDLING_STATE_NEUTRAL)
- OpenFire(target)
- return
- return ..()
-
-/mob/living/simple_animal/hostile/jungle/seedling/OpenFire()
- WarmupAttack()
-
-/mob/living/simple_animal/hostile/jungle/seedling/proc/WarmupAttack()
- if(combatant_state == SEEDLING_STATE_NEUTRAL)
- combatant_state = SEEDLING_STATE_WARMUP
- walk(src,0)
- update_icons()
- var/target_dist = get_dist(src,target)
- var/living_target_check = isliving(target)
- if(living_target_check)
- if(target_dist > 7)//Offscreen check
- SolarBeamStartup(target)
- return
- if(get_dist(src,target) >= 4 && prob(40))
- SolarBeamStartup(target)
- return
- addtimer(CALLBACK(src, .proc/Volley), 5)
-
-/mob/living/simple_animal/hostile/jungle/seedling/proc/SolarBeamStartup(mob/living/living_target)//It's more like requiem than final spark
- if(combatant_state == SEEDLING_STATE_WARMUP && target)
- combatant_state = SEEDLING_STATE_ACTIVE
- living_target.apply_status_effect(/datum/status_effect/seedling_beam_indicator, src)
- beam_debuff_target = living_target
- playsound(src,'sound/effects/seedling_chargeup.ogg', 100, 0)
- if(get_dist(src,living_target) > 7)
- playsound(living_target,'sound/effects/seedling_chargeup.ogg', 100, 0)
- solar_beam_identifier = world.time
- addtimer(CALLBACK(src, .proc/Beamu, living_target, solar_beam_identifier), 35)
-
-/mob/living/simple_animal/hostile/jungle/seedling/proc/Beamu(mob/living/living_target, beam_id = 0)
- if(combatant_state == SEEDLING_STATE_ACTIVE && living_target && beam_id == solar_beam_identifier)
- if(living_target.z == z)
- update_icons()
- var/obj/effect/temp_visual/solarbeam_killsat/S = new (get_turf(src))
- var/matrix/starting = matrix()
- starting.Scale(1,32)
- starting.Translate(0,520)
- S.transform = starting
- var/obj/effect/temp_visual/solarbeam_killsat/K = new (get_turf(living_target))
- var/matrix/final = matrix()
- final.Scale(1,32)
- final.Translate(0,512)
- K.transform = final
- living_target.adjustFireLoss(30)
- living_target.adjust_fire_stacks(0.2)//Just here for the showmanship
- living_target.IgniteMob()
- playsound(living_target,'sound/weapons/sear.ogg', 50, 1)
- addtimer(CALLBACK(src, .proc/AttackRecovery), 5)
- return
- AttackRecovery()
-
-/mob/living/simple_animal/hostile/jungle/seedling/proc/Volley()
- if(combatant_state == SEEDLING_STATE_WARMUP && target)
- combatant_state = SEEDLING_STATE_ACTIVE
- update_icons()
- var/datum/callback/cb = CALLBACK(src, .proc/InaccurateShot)
- for(var/i in 1 to 13)
- addtimer(cb, i)
- addtimer(CALLBACK(src, .proc/AttackRecovery), 14)
-
-/mob/living/simple_animal/hostile/jungle/seedling/proc/InaccurateShot()
- if(!QDELETED(target) && combatant_state == SEEDLING_STATE_ACTIVE && !stat)
- if(get_dist(src,target) <= 3)//If they're close enough just aim straight at them so we don't miss at point blank ranges
- Shoot(target)
- return
- var/turf/our_turf = get_turf(src)
- var/obj/item/projectile/seedling/readied_shot = new /obj/item/projectile/seedling(our_turf)
- readied_shot.current = our_turf
- readied_shot.starting = our_turf
- readied_shot.firer = src
- readied_shot.original = target
- readied_shot.yo = target.y - our_turf.y + rand(-1,1)
- readied_shot.xo = target.x - our_turf.x + rand(-1,1)
- readied_shot.fire()
- playsound(src, projectilesound, 100, 1)
-
-/mob/living/simple_animal/hostile/jungle/seedling/proc/AttackRecovery()
- if(combatant_state == SEEDLING_STATE_ACTIVE)
- combatant_state = SEEDLING_STATE_RECOVERY
- update_icons()
- ranged_cooldown = world.time + ranged_cooldown_time
- if(target)
- face_atom(target)
- addtimer(CALLBACK(src, .proc/ResetNeutral), 10)
-
-/mob/living/simple_animal/hostile/jungle/seedling/proc/ResetNeutral()
- combatant_state = SEEDLING_STATE_NEUTRAL
- if(target && !stat)
- update_icons()
- Goto(target, move_to_delay, minimum_distance)
-
-/mob/living/simple_animal/hostile/jungle/seedling/adjustHealth()
- . = ..()
- if(combatant_state == SEEDLING_STATE_ACTIVE && beam_debuff_target)
- beam_debuff_target.remove_status_effect(/datum/status_effect/seedling_beam_indicator)
- beam_debuff_target = null
- solar_beam_identifier = 0
- AttackRecovery()
-
-/mob/living/simple_animal/hostile/jungle/seedling/update_icons()
- . = ..()
- if(!stat)
- switch(combatant_state)
- if(SEEDLING_STATE_NEUTRAL)
- icon_state = "seedling"
- if(SEEDLING_STATE_WARMUP)
- icon_state = "seedling_charging"
- if(SEEDLING_STATE_ACTIVE)
- icon_state = "seedling_fire"
- if(SEEDLING_STATE_RECOVERY)
- icon_state = "seedling"
-
-/mob/living/simple_animal/hostile/jungle/seedling/GiveTarget()
- if(target)
- if(combatant_state == SEEDLING_STATE_WARMUP || combatant_state == SEEDLING_STATE_ACTIVE)//So it doesn't 180 and blast you in the face while it's firing at someone else
- return
- return ..()
-
-/mob/living/simple_animal/hostile/jungle/seedling/LoseTarget()
- if(combatant_state == SEEDLING_STATE_WARMUP || combatant_state == SEEDLING_STATE_ACTIVE)
- return
- return ..()
-
-#undef SEEDLING_STATE_NEUTRAL
-#undef SEEDLING_STATE_WARMUP
-#undef SEEDLING_STATE_ACTIVE
-#undef SEEDLING_STATE_RECOVERY
\ No newline at end of file
diff --git a/icons/mob/jungle/arachnid.dmi b/icons/mob/jungle/arachnid.dmi
index ccd52eeb7d..23482d00eb 100644
Binary files a/icons/mob/jungle/arachnid.dmi and b/icons/mob/jungle/arachnid.dmi differ
diff --git a/icons/mob/jungle/leaper.dmi b/icons/mob/jungle/leaper.dmi
new file mode 100644
index 0000000000..39f807a191
Binary files /dev/null and b/icons/mob/jungle/leaper.dmi differ
diff --git a/icons/mob/jungle/mook.dmi b/icons/mob/jungle/mook.dmi
new file mode 100644
index 0000000000..c9265b22a0
Binary files /dev/null and b/icons/mob/jungle/mook.dmi differ
diff --git a/icons/mob/jungle/seedling.dmi b/icons/mob/jungle/seedling.dmi
new file mode 100644
index 0000000000..01e91c6c29
Binary files /dev/null and b/icons/mob/jungle/seedling.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 3b58a075e4..c3775a64ec 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1784,7 +1784,6 @@
#include "code\modules\mob\living\simple_animal\hostile\hivebot.dm"
#include "code\modules\mob\living\simple_animal\hostile\hostile.dm"
#include "code\modules\mob\living\simple_animal\hostile\illusion.dm"
-#include "code\modules\mob\living\simple_animal\hostile\jungle_mobs.dm"
#include "code\modules\mob\living\simple_animal\hostile\killertomato.dm"
#include "code\modules\mob\living\simple_animal\hostile\mecha_pilot.dm"
#include "code\modules\mob\living\simple_animal\hostile\mimic.dm"
diff --git a/tgstation.dme.rej b/tgstation.dme.rej
new file mode 100644
index 0000000000..a5ff4dc3cb
--- /dev/null
+++ b/tgstation.dme.rej
@@ -0,0 +1,13 @@
+diff a/tgstation.dme b/tgstation.dme (rejected hunks)
+@@ -1746,6 +1745,11 @@
+ #include "code\modules\mob\living\simple_animal\hostile\gorilla\emotes.dm"
+ #include "code\modules\mob\living\simple_animal\hostile\gorilla\gorilla.dm"
+ #include "code\modules\mob\living\simple_animal\hostile\gorilla\visuals_icons.dm"
++#include "code\modules\mob\living\simple_animal\hostile\jungle\_jungle_mobs.dm"
++#include "code\modules\mob\living\simple_animal\hostile\jungle\leaper.dm"
++#include "code\modules\mob\living\simple_animal\hostile\jungle\mega_arachnid.dm"
++#include "code\modules\mob\living\simple_animal\hostile\jungle\mook.dm"
++#include "code\modules\mob\living\simple_animal\hostile\jungle\seedling.dm"
+ #include "code\modules\mob\living\simple_animal\hostile\megafauna\blood_drunk_miner.dm"
+ #include "code\modules\mob\living\simple_animal\hostile\megafauna\bubblegum.dm"
+ #include "code\modules\mob\living\simple_animal\hostile\megafauna\colossus.dm"