mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-21 11:07:46 +01:00
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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user