Jungle Redgate and New Vore Mobs (#15889)

* [WIP] New Vore Mobs

Added new vore mob: Cave Stalker, a 6 legged canine/feline like creature that charges at prey from a distance to pounce them. It immediately stops on the spot if spotted and then retreats quickly if stared down.

* Fixed missing file

* More mobs, still WIP

* More stuff! [Still WIP]

Added another mob called the sonadile that works based on sound similar to the lurker.

Added a bunch of mobs to the maint pred list, including the kururak because that's a cool one.

* Update vorestation.dme

* Update sonadile.dm

* Another mob and stalker tweaks, still WIP, need to add flavour text for all mobs

* Update devil.dm

* Update stalker.dm

* Even more maint pred options

* More Capture crystal mobs

* Finishing touches, no longer WIP!

* Update vore_raptor.dmi

* Added the jungle map redgate

Also resolves a runtime with the scrubble.

Forces jungle redgate only for the first few days at least.

* Update jungle.dmm

* Update jungle_underground.dmm

* Update paint.dm

Fixes paint runtime
This commit is contained in:
SatinIsle
2024-04-07 10:34:24 +10:00
committed by GitHub
parent cd0cfd0dd7
commit 8b71cf692f
22 changed files with 49370 additions and 9 deletions
+90
View File
@@ -1077,6 +1077,96 @@
name = "Fantasy house"
icon_state = "green"
//WELCOME TO THE JUNGLE
/area/redgate/jungle
name = "Jungle"
icon_state = "red"
requires_power = 0
/area/redgate/jungle/aboveground
name = "Jungle"
icon_state = "red"
/area/redgate/jungle/temple
name = "Jungle temple"
icon_state = "yellow"
/area/redgate/jungle/westcaves
name = "Jungle west caves"
icon_state = "yellow"
/area/redgate/jungle/eastcaves
name = "Jungle east caves"
icon_state = "yellow"
/area/redgate/jungle/southcaves
name = "Jungle south caves"
icon_state = "yellow"
/area/redgate/jungle/deepforest
name = "Jungle deep forest"
icon_state = "yellow"
/area/redgate/jungle/facilitynw
name = "Jungle facility north-west"
icon_state = "yellow"
lightswitch = 0
/area/redgate/jungle/facilityne
name = "Jungle facility north-east"
icon_state = "yellow"
lightswitch = 0
/area/redgate/jungle/facilitysw
name = "Jungle facility south-west"
icon_state = "yellow"
lightswitch = 0
/area/redgate/jungle/facilityse
name = "Jungle facility south-east"
icon_state = "yellow"
lightswitch = 0
/area/redgate/jungle/underwaterwest
name = "Jungle underwater west"
icon_state = "purple"
forced_ambience = list('sound/effects/underwater.ogg')
/area/redgate/jungle/underwaterswamp
name = "Jungle underwater swamp"
icon_state = "purple"
forced_ambience = list('sound/effects/underwater.ogg')
/area/redgate/jungle/underwaterpong
name = "Jungle underwater pond"
icon_state = "purple"
forced_ambience = list('sound/effects/underwater.ogg')
/area/redgate/jungle/underwatercave
name = "Jungle underwater cave"
icon_state = "yellow"
/area/redgate/jungle/underwatercaveswamp
name = "Jungle underwater swamp cave"
icon_state = "yellow"
/area/redgate/jungle/underwatercavepond
name = "Jungle underwater pond cave"
icon_state = "yellow"
/area/redgate/jungle/murderroom
name = "Jungle trophy room"
icon_state = "yellow"
/area/redgate/jungle/facilitybar
name = "Jungle facility bar"
icon_state = "yellow"
/area/redgate/jungle/facilitycasino
name = "Jungle facility casino"
icon_state = "yellow"
//HIIIIGHWAY TO THE! LASER-DOME!
/area/redgate/laserdome
name = "Laserdome Safe Zone"
@@ -0,0 +1,267 @@
/mob/living/simple_mob/vore/devil
name = "Statue of Temptation"
desc = "A tall statue made of red-tinted metal in the shape of some sort of demon or devil."
catalogue_data = list(/datum/category_item/catalogue/fauna/devil)
tt_desc = "Metal Statue"
icon = 'icons/mob/vore64x64.dmi'
icon_dead = "devil-dead"
icon_living = "devil"
icon_state = "devil"
icon_rest = "devil"
faction = "devil"
old_x = -16
old_y = 0
default_pixel_x = -16
pixel_x = -16
pixel_y = 0
friendly = list("nudges", "sniffs on", "rumbles softly at", "nuzzles")
response_help = "bumps"
response_disarm = "shoves"
response_harm = "attacks"
movement_cooldown = 0
harm_intent_damage = 7
melee_damage_lower = 3
melee_damage_upper = 10
maxHealth = 100
attacktext = list("bites")
see_in_dark = 8
minbodytemp = 0
ai_holder_type = /datum/ai_holder/simple_mob/vore/devil
vore_bump_chance = 25
vore_digest_chance = 50
vore_escape_chance = 5
vore_pounce_chance = 1000
vore_active = 1
vore_icons = 1
vore_icons = SA_ICON_LIVING | SA_ICON_REST
vore_capacity = 1
swallowTime = 50
vore_ignores_undigestable = TRUE
vore_default_mode = DM_DIGEST
vore_pounce_maxhealth = 1000
vore_bump_emote = "pounces on"
/mob/living/simple_mob/vore/devil/init_vore()
..()
var/obj/belly/B = vore_selected
B.name = "stomach"
B.desc = "It turns out that this was not just any old statue, but some form of android waiting for its chance to ambush you. The moment that it laid its hands on you, your fate was decided. The jaws of the machine parted, if you could call them that, and immediately enveloped your head. The inside was hot and slick, but dry. The textures were startlingly realistic, the base was clearly a tongue, the top palate of the mouth was hard but somewhat pliable. Not that you had time to admire it before the rest of your body was stuffed inside. Through a short passage down through a rubbery tube of a gullet, mechanical contractions squeezing you down from behind, you're quickly deposited in something much resembling a stomach. Amid the sounds of mechanical whirrs, you can heard glorping, gurgling and burbling from unknown sources. The walls wrap firmly around your body, deliberately dramping you up into the smallest space that the machine can crush you into, whilst the synthetic lining around you ripples across your hunched up form. You can even see yourself, the gut itself is backlit by some eerie red glow, just enough to tell exactly what is happening to you. It doesn't help that you can see the drooling fluids glistening in the dim light."
B.mode_flags = DM_FLAG_THICKBELLY
B.belly_fullscreen = "a_synth_flesh_mono_hole"
B.digest_brute = 2
B.digest_burn = 2
B.digest_oxy = 1
B.digestchance = 100
B.absorbchance = 0
B.escapechance = 5
B.selective_preference = DM_DIGEST
B.escape_stun = 5
/datum/category_item/catalogue/fauna/devil
name = "Extra-Realspace Machine - Statue of Temptation"
desc = "Classification: Synthetic Lifeform\
<br><br>\
The origin of this machine is not well understood, neither is its purpose nor whether it is sapient. However, we have been able to study a little about their behaviour. \
These creatures seem to disquise themselves as statues, making no movement what so ever when being directly observed. There is little to suggest they move at all when there is nobody present either. \
However, when lifeforms exist nearby, these oddly curvaceous devils spring to life, lighting up and attempting to devour all living things. We assume they reduce their targets to biofuel to sustain themselves, but they have been known to break their disguise when attacked to defend themselves."
value = CATALOGUER_REWARD_HARD
/mob/living/simple_mob/vore/devil/PounceTarget(var/mob/living/M, var/successrate = 100)
vore_pounce_cooldown = world.time + 1 SECONDS // don't attempt another pounce for a while
if(prob(successrate)) // pounce success!
M.Weaken(5)
M.visible_message("<span class='danger'>\The [src] pounces on \the [M]!</span>!")
else // pounce misses!
M.visible_message("<span class='danger'>\The [src] attempts to pounce \the [M] but misses!</span>!")
playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
if(will_eat(M) && (!M.canmove || vore_standing_too)) //if they're edible then eat them too
return EatTarget(M)
else
return //just leave them
//AI
/datum/ai_holder/simple_mob/vore/devil
wander = FALSE
/datum/ai_holder/simple_mob/vore/devil/proc/check_witness(list/possible_targets, has_targets_list)
if(!has_targets_list)
possible_targets = list_targets()
for(var/mob/living/L in possible_targets)
if(!check_attacker(L) && !L.stat) //If that person is awake and hasn't attacked the mob.
if((L.dir == 1 && holder.y >= L.y) || (L.dir == 2 && holder.y <= L.y) || (L.dir == 4 && holder.x >= L.x) || (L.dir == 8 && holder.x <= L.x)) //stop attacking if they look at you
return TRUE
return FALSE
/datum/ai_holder/simple_mob/vore/devil/find_target(list/possible_targets, has_targets_list)
if(!vore_hostile)
return ..()
if(!isanimal(holder)) //Only simplemobs have the vars we need
return ..()
var/mob/living/simple_mob/vore/H = holder
if(H.vore_fullness >= H.vore_capacity) //Don't beat people up if we're full
return ..()
ai_log("find_target() : Entered.", AI_LOG_TRACE)
. = list()
if(!has_targets_list)
possible_targets = list_targets()
var/list/valid_mobs = list()
for(var/mob/living/possible_target in possible_targets)
// if(check_witness())
// set_stance(STANCE_IDLE)
// continue
if(!can_attack(possible_target))
continue
. |= possible_target
if(!isliving(possible_target))
continue
if(vore_check(possible_target))
valid_mobs |= possible_target
var/new_target
if(valid_mobs.len)
if(H.icon_state != "devil_target" || H.icon_state != "devil_target-1")
H.icon_living = "devil_target"
H.update_icon()
new_target = pick(valid_mobs)
else if(hostile)
if(H.icon_state != "devil_target" || H.icon_state != "devil_target-1")
H.icon_living = "devil_target"
H.update_icon()
new_target = pick(.)
if(!new_target)
if(H.icon_state == "devil_target" || H.icon_state == "devil_target-1")
H.icon_living = "devil"
H.update_icon()
return null
give_target(new_target)
return new_target
/datum/ai_holder/simple_mob/vore/devil/can_attack(atom/movable/the_target, var/vision_required = TRUE)
ai_log("can_attack() : Entering.", AI_LOG_TRACE)
if(!can_see_target(the_target) && vision_required)
return FALSE
if(!belly_attack)
if(isbelly(holder.loc))
return FALSE
if(check_witness()) //If anyone is looking, cancel the attack
set_stance(STANCE_IDLE)
return FALSE
if(isliving(the_target))
var/mob/living/L = the_target
if(ishuman(L) || issilicon(L))
if(L.key && !L.client) // SSD players get a pass
return FALSE
if(L.stat)
if(L.stat == DEAD && !handle_corpse) // Leave dead things alone
return FALSE
if(L.stat == UNCONSCIOUS) // Do we have mauling? Yes? Then maul people who are sleeping but not SSD
if(mauling)
return TRUE
//VOREStation Add Start
else if(unconscious_vore && L.allowmobvore)
var/mob/living/simple_mob/vore/eater = holder
if(eater.will_eat(L))
return TRUE
else
return FALSE
//VOREStation Add End
else
return FALSE
// if(!check_attacker(L))
// if((L.dir == 1 && holder.y >= L.y) || (L.dir == 2 && holder.y <= L.y) || (L.dir == 4 && holder.x >= L.x) || (L.dir == 8 && holder.x <= L.x)) //stop attacking if they look at you
// set_stance(STANCE_IDLE)
// return FALSE
//VOREStation add start
else if(forgive_resting && !isbelly(holder.loc)) //Doing it this way so we only think about the other conditions if the var is actually set
if((holder.health == holder.maxHealth) && !hostile && (L.resting || L.weakened || L.stunned)) //If our health is full, no one is fighting us, we can forgive
var/mob/living/simple_mob/vore/eater = holder
if(!eater.will_eat(L)) //We forgive people we can eat by eating them
set_stance(STANCE_IDLE)
return FALSE //Forgiven
//VOREStation add end
if(holder.IIsAlly(L))
return FALSE
return TRUE
if(istype(the_target, /obj/mecha))
var/obj/mecha/M = the_target
if(M.occupant)
return can_attack(M.occupant)
return destructive // Empty mechs are 'neutral'.
if(istype(the_target, /obj/machinery/porta_turret))
var/obj/machinery/porta_turret/P = the_target
if(P.stat & BROKEN)
return FALSE // Already dead.
if(P.faction == holder.faction)
return FALSE // Don't shoot allied turrets.
if(!P.raised && !P.raising)
return FALSE // Turrets won't get hurt if they're still in their cover.
return TRUE
if(istype(the_target, /obj/structure/blob)) // Blob mobs are always blob faction, but the blob can anger other things.
var/obj/structure/blob/Blob = the_target
if(holder.faction == Blob.faction)
return FALSE
return TRUE
/datum/ai_holder/simple_mob/vore/devil/engage_target()
ai_log("engage_target() : Entering.", AI_LOG_DEBUG)
// Can we still see them?
if(!target || !can_attack(target))
ai_log("engage_target() : Lost sight of target.", AI_LOG_TRACE)
if(lose_target()) // We lost them (returns TRUE if we found something else to do)
ai_log("engage_target() : Pursuing other options (last seen, or a new target).", AI_LOG_TRACE)
return
var/distance = get_dist(holder, target)
ai_log("engage_target() : Distance to target ([target]) is [distance].", AI_LOG_TRACE)
holder.face_atom(target)
last_conflict_time = world.time
// Do a 'special' attack, if one is allowed.
// if(prob(special_attack_prob) && (distance >= special_attack_min_range) && (distance <= special_attack_max_range))
if(holder.ICheckSpecialAttack(target))
ai_log("engage_target() : Attempting a special attack.", AI_LOG_TRACE)
on_engagement(target)
if(special_attack(target)) // If this fails, then we try a regular melee/ranged attack.
ai_log("engage_target() : Successful special attack. Exiting.", AI_LOG_DEBUG)
return
// Stab them.
else if(distance <= 1 && !pointblank)
ai_log("engage_target() : Attempting a melee attack.", AI_LOG_TRACE)
on_engagement(target)
melee_attack(target)
else if(distance <= 1 && !holder.ICheckRangedAttack(target)) // Doesn't have projectile, but is pointblank
ai_log("engage_target() : Attempting a melee attack.", AI_LOG_TRACE)
on_engagement(target)
melee_attack(target)
// Shoot them.
else if(holder.ICheckRangedAttack(target) && (distance <= max_range(target)) )
on_engagement(target)
if(firing_lanes && !test_projectile_safety(target))
// Nudge them a bit, maybe they can shoot next time.
var/turf/T = get_step(holder, pick(cardinal))
if(T)
holder.IMove(T) // IMove() will respect movement cooldown.
holder.face_atom(target)
ai_log("engage_target() : Could not safely fire at target. Exiting.", AI_LOG_DEBUG)
return
ai_log("engage_target() : Attempting a ranged attack.", AI_LOG_TRACE)
ranged_attack(target)
// Run after them.
else if(!stand_ground)
ai_log("engage_target() : Target ([target]) too far away. Exiting.", AI_LOG_DEBUG)
set_stance(STANCE_APPROACH)
@@ -0,0 +1,189 @@
/////////////////////////////////////Man-Trap/////////////////////////////////////////
/mob/living/simple_mob/vore/mantrap
name = "Mantrap"
desc = "This massive plant lays in wait in the brush, and can be very difficult to spot. When laying open on the ground, the trap mechanism resembles a massive saw-toothed maw."
catalogue_data = list(/datum/category_item/catalogue/fauna/mantrap)
tt_desc = "Dionaea Humanis"
icon = 'icons/mob/vore.dmi'
icon_dead = "flytrap-dead"
icon_living = "flytrap"
icon_state = "flytrap"
icon_rest = "flytrap"
faction = "plants"
harm_intent_damage = 0
melee_damage_lower = 0
melee_damage_upper = 0
maxHealth = 100
attacktext = list("flinches at")
see_in_dark = 8
minbodytemp = 0
ai_holder_type = /datum/ai_holder/simple_mob/passive/mantrap
density = 0
anchored = 1
vore_bump_chance = 100
vore_digest_chance = 50
vore_escape_chance = 5
vore_pounce_chance = 1000
vore_active = 1
vore_icons = 1
vore_icons = SA_ICON_LIVING | SA_ICON_REST
vore_capacity = 1
swallowTime = 1
vore_ignores_undigestable = TRUE
vore_default_mode = DM_SELECT
vore_pounce_maxhealth = 1000
vore_bump_emote = "encloses on"
/mob/living/simple_mob/vore/mantrap/init_vore()
..()
var/obj/belly/B = vore_selected
B.name = "trap"
B.desc = "As you step onto the large leaves of the mantrap, they suddenly shoot up and snap shut around you, encasing you in a fleshy-feeling gut. The saw-toothed spikes around the edge of the leaves interlock with one another and exerts a tremendous pressure on your body. Copious volumes of fluids begin to seep in from the walls themselves, rapidly coating your body and pooling around you, all of your movements only seem to speed up this process.."
B.mode_flags = DM_FLAG_THICKBELLY
B.belly_fullscreen = "destination_tumby"
B.belly_fullscreen_color = "#02a802"
B.digest_brute = 2
B.digest_burn = 2
B.digest_oxy = 1
B.digestchance = 100
B.absorbchance = 0
B.escapechance = 10
B.selective_preference = DM_SELECT
B.escape_stun = 10
/datum/category_item/catalogue/fauna/mantrap
name = "Extra-Realspace Flora - Mantrap"
desc = "Classification: Dionaea Humanis\
<br><br>\
The mantrap is a rare carnivorous plant found in redgate locations. The main component of which is a pair of large fan-shaped leaves with serrated dagger like edges, which act as it's main predation mechanism. \
Despite its size, it is a patient predator that will lay in wait for multiple months before claiming a meal. Whilst it is able to rapidly digest creatures of significant sizes, its metabolism is slow and the nutrients it prey provides can sustain it for a long period of time."
value = CATALOGUER_REWARD_HARD
/datum/ai_holder/simple_mob/passive/mantrap
vision_range = 1
wander = FALSE
/mob/living/simple_mob/vore/mantrap/Crossed(var/atom/movable/AM) // Transplanting this from /mob/living/carbon/human/Crossed()
if(AM == src || AM.is_incorporeal()) // We're not going to run over ourselves or ghosts
return
if(isliving(AM))
var/mob/living/L = AM
if(L.devourable && L.allowmobvore && (src.vore_fullness < src.vore_capacity))
perform_the_nom(src,L,src,src.vore_selected,1)
return
else
return
////////////////////////////PITCHER PLANT////////////////////////////////////////////////
/mob/living/simple_mob/vore/pitcher
name = "Pitcher Plant"
desc = "This large pitcher plant looks big enough to fit an entire person, with a little stretching. Long tendrils rest at the entrance."
catalogue_data = list(/datum/category_item/catalogue/fauna/pitcher)
tt_desc = "Nepenthes Titanis"
icon = 'icons/mob/vore.dmi'
icon_dead = "pitcher-dead"
icon_living = "pitcher"
icon_state = "pitcher"
icon_rest = "pitcher"
faction = "plants"
movement_cooldown = 0
harm_intent_damage = 0
melee_damage_lower = 0
melee_damage_upper = 0
maxHealth = 100
attacktext = list("flinches at")
see_in_dark = 8
minbodytemp = 0
ai_holder_type = /datum/ai_holder/simple_mob/vore/pitcher
density = 0
anchored = 1
vore_bump_chance = 100
vore_digest_chance = 50
vore_escape_chance = 5
vore_pounce_chance = 0
vore_active = 1
vore_icons = 1
vore_icons = SA_ICON_LIVING | SA_ICON_REST
vore_capacity = 1
swallowTime = 1
vore_ignores_undigestable = TRUE
vore_default_mode = DM_SELECT
vore_pounce_maxhealth = 0
vore_bump_emote = "encloses on"
appendage_color = "#03a319"
base_attack_cooldown = 5 SECONDS
projectiletype = /obj/item/projectile/beam/appendage
projectilesound = 'sound/effects/slime_squish.ogg'
/mob/living/simple_mob/vore/pitcher/init_vore()
..()
var/obj/belly/B = vore_selected
B.name = "stomach"
B.desc = "Walking a little too close to the pitcher plant, you trigger its trap mechanism and a tendril shoots out towards you. Wrapping around your body, you are rapidly dragged into the open mouth of the plant, stuffing your entire body into a fleshy, green stomach filled with a pool of some sort of tingling liquid. The lid of the plant slams down over the mouth, making it far more difficult to escape, all whilst that pool steadily seems to be filling up."
B.mode_flags = DM_FLAG_THICKBELLY
B.belly_fullscreen = "destination_tumby"
B.belly_fullscreen_color = "#02a802"
B.digest_brute = 1
B.digest_burn = 1
B.digest_oxy = 0
B.digestchance = 100
B.absorbchance = 0
B.escapechance = 10
B.selective_preference = DM_SELECT
B.escape_stun = 10
/datum/category_item/catalogue/fauna/pitcher
name = "Extra-Realspace Flora - Titan Pitcher Plant"
desc = "Classification: Nepenthes Titanis\
<br><br>\
The titan pitcher plant is a rare carnivorous plant found in redgate locations. Unlike typical pitfall type pitcher plants that we are familiar with in realspace, these variants are far more aggressive. \
When their defense mechanisms are triggered, by sensing vibrations through their extensive root systems, long tendrils emerge from the mouth of these plants to ensnare living creatures nearby. These creatures are dragged into the stomach-like bell shaped cavity of the plant and the rigid leaf closes as a lid. \
Despite the size of these plants, they are perfectly capable of trapping an adult human and should be treated with heavy caution. More typically, they predate on small mammals and reptiles in their local ecological environment."
value = CATALOGUER_REWARD_HARD
/datum/ai_holder/simple_mob/vore/pitcher
vision_range = 3
wander = FALSE
retaliate = FALSE
pointblank = TRUE
/*/mob/living/simple_mob/vore/pitcher/do_special_attack(atom/A)
. = TRUE
if(ckey)
return
tongue(A)
/mob/living/simple_mob/vore/pitcher/proc/tongue(atom/A)
var/obj/item/projectile/P = new /obj/item/projectile/beam/appendage(get_turf(src))
src.visible_message("<span class='danger'>\The [src] launches a green appendage at \the [A]!</span>")
playsound(src, "sound/effects/slime_squish.ogg", 50, 1)
P.launch_projectile(A, BP_TORSO, src)*/
//NEVER MOVE!!!
/datum/ai_holder/simple_mob/vore/pitcher/walk_to_destination()
return
/datum/ai_holder/simple_mob/vore/pitcher/give_destination()
return
/datum/ai_holder/simple_mob/vore/pitcher/walk_path()
return
/datum/ai_holder/simple_mob/vore/pitcher/move_once()
return
/datum/ai_holder/simple_mob/vore/pitcher/handle_wander_movement()
return
/datum/ai_holder/simple_mob/vore/pitcher/walk_to_target()
return
@@ -0,0 +1,141 @@
/mob/living/simple_mob/vore/scrubble
name = "Scrubble"
desc = "A small skittish animal with some features resembling rodents and foxes. Usually seen coated with beige and brown fur, the scrubble has four ears that pivot quickly, two long fluffy tails and dark red eyes."
catalogue_data = list(/datum/category_item/catalogue/fauna/scrubble)
tt_desc = "vuldentia"
icon = 'icons/mob/vore.dmi'
icon_dead = "scrubble-dead"
icon_living = "scrubble"
icon_state = "scrubble"
icon_rest = "scrubble-rest"
faction = "scrubble"
friendly = list("nudges", "sniffs on", "rumbles softly at", "nuzzles")
response_help = "bumps"
response_disarm = "shoves"
response_harm = "attacks"
movement_cooldown = 0
harm_intent_damage = 2
melee_damage_lower = 1
melee_damage_upper = 4
maxHealth = 50
attacktext = list("bites")
see_in_dark = 8
minbodytemp = 0
ai_holder_type = /datum/ai_holder/simple_mob/hostile/scrubble
say_list_type = /datum/say_list/scrubble
vore_bump_chance = 25
vore_digest_chance = 50
vore_escape_chance = 5
vore_pounce_chance = 1000
vore_active = 1
vore_icons = 1
vore_icons = SA_ICON_LIVING | SA_ICON_REST
vore_capacity = 1
swallowTime = 50
vore_ignores_undigestable = TRUE
vore_default_mode = DM_DIGEST
vore_pounce_maxhealth = 1000
vore_bump_emote = "pounces on"
/mob/living/simple_mob/vore/scrubble/init_vore()
..()
var/obj/belly/B = vore_selected
B.name = "stomach"
B.desc = "Despite the small size of the scrubble, it seems to have a lot of energy behind it. The critter dives atop you in a panic, it's maw quickly engulfing your head as it's paws flail scrabble against you, hot slobber slathering across your tightly trapped face. It takes a little repositioning to get itself in the right position, but soon the creature is gulping its way down your entire body. Somehow it manages to squeeze you completely into a gut that should rightly be far too small for anything but a mouse, bundling up your body into a tight ball as the walls around you clench in tightly to keep you nice and compact. The sounds of burbling and glorping echo through the intensely tight space as the stomach lining grinds in thick oozes against your skin, pressure so high that you can barely move a muscle."
B.mode_flags = DM_FLAG_THICKBELLY
B.belly_fullscreen = "yet_another_tumby"
B.digest_brute = 1
B.digest_burn = 1
B.digest_oxy = 0
B.digestchance = 100
B.absorbchance = 0
B.escapechance = 15
B.selective_preference = DM_DIGEST
B.escape_stun = 5
/datum/say_list/scrubble
emote_hear = list("yips","squeaks","chirps")
emote_see = list("looks around frantically","sniffs at the air","wafts its tails","flickers its four ears")
/datum/category_item/catalogue/fauna/scrubble
name = "Extra-Realspace Fauna - Scrubble"
desc = "Classification: Vuldentia\
<br><br>\
The Scrubble is a small creature found in redgate locations that has properties resembling those of rodents and vulpines. \
Primarily a scavanger, the scrubble is known to eat all sorts of organic material, whether it's vegetable or meat, but generally does not hunt on its own. Largely defenseless against larger creature, the scrubble is certainly a prey species. \
Their geneneral behaviour supports this, as they will heavily avoid interaction with any other species that approach it, usually darting away rapidly and keeping a distance. However, they have been known to act defensively should they be completely cornered."
value = CATALOGUER_REWARD_HARD
/mob/living/simple_mob/vore/scrubble/PounceTarget(var/mob/living/M, var/successrate = 100)
vore_pounce_cooldown = world.time + 1 SECONDS // don't attempt another pounce for a while
if(prob(successrate)) // pounce success!
M.Weaken(5)
M.visible_message("<span class='danger'>\The [src] pounces on \the [M]!</span>!")
else // pounce misses!
M.visible_message("<span class='danger'>\The [src] attempts to pounce \the [M] but misses!</span>!")
playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
if(will_eat(M) && (!M.canmove || vore_standing_too)) //if they're edible then eat them too
return EatTarget(M)
else
return //just leave them
//AI
/datum/ai_holder/simple_mob/hostile/scrubble
can_flee = TRUE
vision_range = 5 //Only react if you get close
can_flee = TRUE // If they're even allowed to flee.
flee_when_dying = TRUE // If they should flee when low on health.
dying_threshold = 1.1 // Flee at max health
base_wander_delay = 2
/datum/ai_holder/simple_mob/hostile/scrubble/flee_from_target()
ai_log("flee_from_target() : Entering.", AI_LOG_DEBUG)
if(!target || !should_flee() || !can_attack(target)) // can_attack() is used since it checks the same things we would need to anyways.
ai_log("flee_from_target() : Lost target to flee from.", AI_LOG_INFO)
lose_target()
set_stance(STANCE_IDLE)
ai_log("flee_from_target() : Exiting.", AI_LOG_DEBUG)
return
var/mob/living/simple_mob/vore/H = holder
var/mob/living/L = target
var/distance = get_dist(holder, target)
if(distance <= 1)
if(L.devourable && L.allowmobvore && (H.vore_fullness < H.vore_capacity))
H.face_atom(L)
H.PounceTarget(L)
return
ai_log("flee_from_target() : Stepping away.", AI_LOG_TRACE)
step_away(holder, target, 7)
ai_log("flee_from_target() : Exiting.", AI_LOG_DEBUG)
/datum/ai_holder/simple_mob/hostile/scrubble/find_target(list/possible_targets, has_targets_list)
if(!isanimal(holder)) //Only simplemobs have the vars we need
return ..()
var/list/L = list()
if(!has_targets_list)
possible_targets = list_targets()
var/list/valid_mobs = list()
for(var/mob/living/possible_target in possible_targets)
if(!can_attack(possible_target))
continue
L |= possible_target
if(!isliving(possible_target))
continue
if(vore_check(possible_target))
valid_mobs |= possible_target
var/new_target
if(valid_mobs.len)
new_target = pick(valid_mobs)
else if(hostile && L.len)
new_target = pick(L)
if(!new_target)
return null
give_target(new_target)
return new_target
@@ -0,0 +1,102 @@
/mob/living/simple_mob/vore/sonadile
name = "Sonadile"
desc = "A tall, oddly proportioned bipedal reptile. Whilst its body is fairly large on its own, the incredibly long neck brings its height up to near 14 feet tall. Covered in green scales with a yellow underbelly, with a long thin tail, short legs and stubby arms. It has orange frills down its spine and the eyes are an odd grey colour, it doesn't appear to be able to see very well."
catalogue_data = list(/datum/category_item/catalogue/fauna/sonadile)
tt_desc = "Crocodylidae"
icon = 'icons/mob/vore64x64.dmi'
icon_dead = "sonadile-dead"
icon_living = "sonadile"
icon_state = "sonadile"
icon_rest = "sonadile"
faction = "sonadile"
old_x = -16
old_y = 0
default_pixel_x = -16
pixel_x = -16
pixel_y = 0
friendly = list("nudges", "sniffs on", "rumbles softly at", "nuzzles")
response_help = "bumps"
response_disarm = "shoves"
response_harm = "attacks"
movement_cooldown = 0
harm_intent_damage = 7
melee_damage_lower = 3
melee_damage_upper = 10
maxHealth = 100
attacktext = list("bites")
see_in_dark = 8
minbodytemp = 0
ai_holder_type = /datum/ai_holder/simple_mob/say_aggro
say_list_type = /datum/say_list/sonadile
vore_bump_chance = 25
vore_digest_chance = 50
vore_escape_chance = 5
vore_pounce_chance = 1000
vore_active = 1
vore_icons = 1
vore_icons = SA_ICON_LIVING | SA_ICON_REST
vore_capacity = 1
swallowTime = 50
vore_ignores_undigestable = TRUE
vore_default_mode = DM_DIGEST
vore_pounce_maxhealth = 1000
vore_bump_emote = "pounces on"
/mob/living/simple_mob/vore/sonadile/init_vore()
..()
var/obj/belly/B = vore_selected
B.name = "stomach"
B.desc = "The creature's huge maw drops down over your body, the long neck preventing it from barely having to shift its torso at all. The jaws quickly travel down you, slathering you in a drool as you're quickly stuffed through the flexible muscle of the throat. In a matter of seconds you are effortlessly lifted from the ground, your entire figure now reduced to a bulge within the neck of the beast, your feet soon vanishing into its mouth with a visceral gulp. The journey down is a long and slow one, the gullet squeezing you steadily along with heavy rippling contractions, the sonadile is quite content that you're heading in the right direction. With every inch, the world around you grows louder with the sound of a heartbeat and the gutteral grumbles of your upcoming destination. Before long you are squeezed down through a tight fleshy valve and deposited in the stomach of the reptile, walls immediately bearing down on you from every direction to ensure that you're tightly confined with little room to move. Hot, humid and slick with all manner of thick and thin liquids, this place isn't treating you any different from whatever else this animal likes to eat."
B.mode_flags = DM_FLAG_THICKBELLY
B.belly_fullscreen = "yet_another_tumby"
B.digest_brute = 2
B.digest_burn = 2
B.digest_oxy = 1
B.digestchance = 100
B.absorbchance = 0
B.escapechance = 5
B.selective_preference = DM_DIGEST
B.escape_stun = 5
/datum/say_list/sonadile
emote_hear = list("warbles","roars","yawns")
emote_see = list("stumbles about clumsily","slowly pans its head around","extends and shakes its frills","lashes its tail against the ground")
/datum/category_item/catalogue/fauna/sonadile
name = "Extra-Realspace Fauna - Sonadile"
desc = "Classification: Crocodylidae\
<br><br>\
The Sonadile is named as such due to it's vague resemblence to crocodilian animals and its enhanced ability to hunt purely through sound. \
Frequently found in dark and damp spaces, the sonadile seems to have little problem moving about despite its unusual size and shape, and generally is seen walking on two legs. Relatively docile when left in the quiet, researchers should be warned that it is quick to attack those who speak out loud. \
Whilst not entirely blind, it appears to have difficulty discerning differences between shapes and movement, but once it hears something that it interprets as prey, it attempts to swallow the creature whole and alive, lashing its head forward on the massively long neck."
value = CATALOGUER_REWARD_HARD
/mob/living/simple_mob/vore/sonadile/update_icon()
. = ..()
if(vore_active)
var/voremob_awake = FALSE
if(icon_state == icon_living)
voremob_awake = TRUE
update_fullness()
if(!vore_fullness)
update_transform()
return 0
else if((stat == CONSCIOUS) && (!icon_rest || !resting || !incapacitated(INCAPACITATION_DISABLED)) && (vore_icons & SA_ICON_LIVING))
icon_state = "[icon_living]-[vore_fullness]"
spawn(100)
if(vore_fullness)
icon_state = "[icon_living]-2"
update_transform()
else if(stat >= DEAD && (vore_icons & SA_ICON_DEAD))
icon_state = "[icon_dead]-[vore_fullness]"
else if(((stat == UNCONSCIOUS) || resting || incapacitated(INCAPACITATION_DISABLED) ) && icon_rest && (vore_icons & SA_ICON_REST))
icon_state = "[icon_rest]-[vore_fullness]"
spawn(100)
if(vore_fullness)
icon_state = "[icon_living]-2"
update_transform()
if(vore_eyes && voremob_awake) //Update eye layer if applicable.
remove_eyes()
add_eyes()
update_transform()
@@ -0,0 +1,277 @@
/mob/living/simple_mob/vore/stalker
name = "Cave Stalker"
desc = "A strange slim creature that lurks in the dark. It's features could be described as a mix of feline and canine, but it's most notable alien property is the second set of forelegs. Additionally, it has a series of boney blue spikes running down it's spine, a similarly hard tip to it's tail and dark blue fangs hanging from it's snout."
catalogue_data = list(/datum/category_item/catalogue/fauna/stalker)
tt_desc = "Canidfelanis"
icon = 'icons/mob/vore64x32.dmi'
icon_dead = "stalker-dead"
icon_living = "stalker"
icon_state = "stalker"
icon_rest = "stalker-rest"
faction = "stalker"
old_x = -16
old_y = 0
default_pixel_x = -16
pixel_x = -16
pixel_y = 0
friendly = list("nudges", "sniffs on", "rumbles softly at", "nuzzles")
response_help = "bumps"
response_disarm = "shoves"
response_harm = "attacks"
movement_cooldown = 0
harm_intent_damage = 7
melee_damage_lower = 3
melee_damage_upper = 10
maxHealth = 100
attacktext = list("bites")
see_in_dark = 8
minbodytemp = 0
ai_holder_type = /datum/ai_holder/simple_mob/vore/stalker
say_list_type = /datum/say_list/stalker
vore_bump_chance = 25
vore_digest_chance = 50
vore_escape_chance = 5
vore_pounce_chance = 1000
vore_active = 1
vore_icons = 1
vore_icons = SA_ICON_LIVING | SA_ICON_REST
vore_capacity = 1
swallowTime = 50
vore_ignores_undigestable = TRUE
vore_default_mode = DM_DIGEST
vore_pounce_maxhealth = 1000
vore_bump_emote = "pounces on"
/mob/living/simple_mob/vore/stalker/init_vore()
..()
var/obj/belly/B = vore_selected
B.name = "stomach"
B.desc = "The lithe creature spends only minimal time with you pinned beneath it, before it's jaws stretch wide ahead of your face. The slightly blue hued interior squelches tightly over your head as the stalker's teeth prod against you, threatening to become much more of a danger if you put up too much of a fight. However, the process is quick, your body is efficiently squeezed through that tight gullet, contractions dragging you effortlessly towards the creature's gut. The stomach swells and hangs beneath the animal, swaying like a hammock under the newfound weight. The walls wrap incredibly tightly around you, compressing you tightly into a small ball as it grinds caustic juices over you."
B.mode_flags = DM_FLAG_THICKBELLY
B.belly_fullscreen = "yet_another_tumby"
B.digest_brute = 2
B.digest_burn = 2
B.digest_oxy = 1
B.digestchance = 100
B.absorbchance = 0
B.escapechance = 5
B.selective_preference = DM_DIGEST
B.escape_stun = 5
/datum/say_list/stalker
emote_hear = list("hisses","growls","chuffs")
emote_see = list("watches you carefully","scratches at the ground","whips it's tail","paces")
/datum/category_item/catalogue/fauna/stalker
name = "Extra-Realspace Fauna - Cave Stalker"
desc = "Classification: Canidfelanis\
<br><br>\
Cave Stalker's an unusual alien animal found at a number of redgate locations, suspected to have originated from locations other than those that they are found at. \
They are carnivorous and highly aggressive beasts that spend the majority of their time skulking in dark locations with long lines of sight, they're known to spend a lot of time stalking their prey to assess their vulnerability. \
Typically they will follow their prey from a distance, and when they are not paying attention, will rush in to tackle their meal. However, they're stealth hunters and are easily startled if spotted. They will not attack their prey head on unless physically provoked to defend themselves."
value = CATALOGUER_REWARD_HARD
/mob/living/simple_mob/vore/stalker/PounceTarget(var/mob/living/M, var/successrate = 100)
vore_pounce_cooldown = world.time + 1 SECONDS // don't attempt another pounce for a while
if(prob(successrate)) // pounce success!
M.Weaken(5)
M.visible_message("<span class='danger'>\The [src] pounces on \the [M]!</span>!")
else // pounce misses!
M.visible_message("<span class='danger'>\The [src] attempts to pounce \the [M] but misses!</span>!")
playsound(src, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
if(will_eat(M) && (!M.canmove || vore_standing_too)) //if they're edible then eat them too
return EatTarget(M)
else
return //just leave them
//AI
/datum/ai_holder/simple_mob/vore/stalker
can_flee = TRUE
vision_range = 15 //They rush you from off-screen, but easily countered if you are aware
var/watched = 0
/datum/ai_holder/simple_mob/vore/stalker/proc/check_witness(list/possible_targets, has_targets_list)
if(!has_targets_list)
possible_targets = list_targets()
for(var/mob/living/L in possible_targets)
var/distance = get_dist(holder, L)
if(!check_attacker(L) && !L.stat && distance <= 9) //Stop approaching just off screen if they're looking in your direction
if((L.dir == 1 && holder.y >= L.y) || (L.dir == 2 && holder.y <= L.y) || (L.dir == 4 && holder.x >= L.x) || (L.dir == 8 && holder.x <= L.x)) //stop attacking if they look at you
set_stance(STANCE_IDLE)
if(!watched)
watched = 1
spawn(40) //run away if they keep staring
watched = 0
if((L.dir == 1 && holder.y >= L.y) || (L.dir == 2 && holder.y <= L.y) || (L.dir == 4 && holder.x >= L.x) || (L.dir == 8 && holder.x <= L.x) && distance <= 8)
if(!L.stat) //If the prey is weakened in any way, don't run
step_away(holder, L, 8) //Flee if they stare at you within normal view range
holder.face_atom(L)
return TRUE
return FALSE
/datum/ai_holder/simple_mob/vore/stalker/find_target(list/possible_targets, has_targets_list)
if(!vore_hostile)
return ..()
if(!isanimal(holder)) //Only simplemobs have the vars we need
return ..()
var/mob/living/simple_mob/H = holder
if(H.vore_fullness >= H.vore_capacity) //Don't beat people up if we're full
return ..()
ai_log("find_target() : Entered.", AI_LOG_TRACE)
. = list()
if(!has_targets_list)
possible_targets = list_targets()
var/list/valid_mobs = list()
for(var/mob/living/possible_target in possible_targets)
if(!can_attack(possible_target))
continue
. |= possible_target
if(!isliving(possible_target))
continue
if(vore_check(possible_target))
valid_mobs |= possible_target
var/new_target
if(valid_mobs.len)
new_target = pick(valid_mobs)
else if(hostile)
new_target = pick(.)
if(!new_target)
return null
give_target(new_target)
return new_target
/datum/ai_holder/simple_mob/vore/stalker/can_attack(atom/movable/the_target, var/vision_required = TRUE)
ai_log("can_attack() : Entering.", AI_LOG_TRACE)
if(!can_see_target(the_target) && vision_required)
return FALSE
if(!belly_attack)
if(isbelly(holder.loc))
return FALSE
if(check_witness())
return FALSE
if(isliving(the_target))
var/mob/living/L = the_target
if(ishuman(L) || issilicon(L))
if(L.key && !L.client) // SSD players get a pass
return FALSE
if(L.stat)
if(L.stat == DEAD && !handle_corpse) // Leave dead things alone
return FALSE
if(L.stat == UNCONSCIOUS) // Do we have mauling? Yes? Then maul people who are sleeping but not SSD
if(mauling)
return TRUE
//VOREStation Add Start
else if(unconscious_vore && L.allowmobvore)
var/mob/living/simple_mob/vore/eater = holder
if(eater.will_eat(L))
return TRUE
else
return FALSE
//VOREStation Add End
else
return FALSE
// if(!check_attacker(L))
// if((L.dir == 1 && holder.y >= L.y) || (L.dir == 2 && holder.y <= L.y) || (L.dir == 4 && holder.x >= L.x) || (L.dir == 8 && holder.x <= L.x)) //stop attacking if they look at you
// set_stance(STANCE_IDLE)
// spawn(40) //run away if they keep staring
// if((L.dir == 1 && holder.y >= L.y) || (L.dir == 2 && holder.y <= L.y) || (L.dir == 4 && holder.x >= L.x) || (L.dir == 8 && holder.x <= L.x))
// if(!L.stat) //If the prey is weakened in any way, don't run
// step_away(holder, L, 8)
// holder.face_atom(L)
// return FALSE
//VOREStation add start
else if(forgive_resting && !isbelly(holder.loc)) //Doing it this way so we only think about the other conditions if the var is actually set
if((holder.health == holder.maxHealth) && !hostile && (L.resting || L.weakened || L.stunned)) //If our health is full, no one is fighting us, we can forgive
var/mob/living/simple_mob/vore/eater = holder
if(!eater.will_eat(L)) //We forgive people we can eat by eating them
set_stance(STANCE_IDLE)
return FALSE //Forgiven
//VOREStation add end
if(holder.IIsAlly(L))
return FALSE
return TRUE
if(istype(the_target, /obj/mecha))
var/obj/mecha/M = the_target
if(M.occupant)
return can_attack(M.occupant)
return destructive // Empty mechs are 'neutral'.
if(istype(the_target, /obj/machinery/porta_turret))
var/obj/machinery/porta_turret/P = the_target
if(P.stat & BROKEN)
return FALSE // Already dead.
if(P.faction == holder.faction)
return FALSE // Don't shoot allied turrets.
if(!P.raised && !P.raising)
return FALSE // Turrets won't get hurt if they're still in their cover.
return TRUE
if(istype(the_target, /obj/structure/blob)) // Blob mobs are always blob faction, but the blob can anger other things.
var/obj/structure/blob/Blob = the_target
if(holder.faction == Blob.faction)
return FALSE
return TRUE
/datum/ai_holder/simple_mob/vore/stalker/engage_target()
ai_log("engage_target() : Entering.", AI_LOG_DEBUG)
// Can we still see them?
if(!target || !can_attack(target))
ai_log("engage_target() : Lost sight of target.", AI_LOG_TRACE)
if(lose_target()) // We lost them (returns TRUE if we found something else to do)
ai_log("engage_target() : Pursuing other options (last seen, or a new target).", AI_LOG_TRACE)
return
var/distance = get_dist(holder, target)
ai_log("engage_target() : Distance to target ([target]) is [distance].", AI_LOG_TRACE)
holder.face_atom(target)
last_conflict_time = world.time
// Do a 'special' attack, if one is allowed.
// if(prob(special_attack_prob) && (distance >= special_attack_min_range) && (distance <= special_attack_max_range))
if(holder.ICheckSpecialAttack(target))
ai_log("engage_target() : Attempting a special attack.", AI_LOG_TRACE)
on_engagement(target)
if(special_attack(target)) // If this fails, then we try a regular melee/ranged attack.
ai_log("engage_target() : Successful special attack. Exiting.", AI_LOG_DEBUG)
return
// Stab them.
else if(distance <= 1 && !pointblank)
ai_log("engage_target() : Attempting a melee attack.", AI_LOG_TRACE)
on_engagement(target)
melee_attack(target)
else if(distance <= 1 && !holder.ICheckRangedAttack(target)) // Doesn't have projectile, but is pointblank
ai_log("engage_target() : Attempting a melee attack.", AI_LOG_TRACE)
on_engagement(target)
melee_attack(target)
// Shoot them.
else if(holder.ICheckRangedAttack(target) && (distance <= max_range(target)) )
on_engagement(target)
if(firing_lanes && !test_projectile_safety(target))
// Nudge them a bit, maybe they can shoot next time.
var/turf/T = get_step(holder, pick(cardinal))
if(T)
holder.IMove(T) // IMove() will respect movement cooldown.
holder.face_atom(target)
ai_log("engage_target() : Could not safely fire at target. Exiting.", AI_LOG_DEBUG)
return
ai_log("engage_target() : Attempting a ranged attack.", AI_LOG_TRACE)
ranged_attack(target)
// Run after them.
else if(!stand_ground)
ai_log("engage_target() : Target ([target]) too far away. Exiting.", AI_LOG_DEBUG)
set_stance(STANCE_APPROACH)