mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2026-07-15 18:22:55 +01:00
More spiders.
This commit is contained in:
+12
-9
@@ -290,16 +290,19 @@
|
||||
|
||||
// More refined version of SA_* ""intelligence"" seperators.
|
||||
// Now includes bitflags, so to target two classes you just do 'MOB_CLASS_ANIMAL|MOB_CLASS_HUMANOID'
|
||||
#define MOB_CLASS_ANIMAL 1 // Simple mobs like saviks and bears.
|
||||
#define MOB_CLASS_HUMANOID 2 // Non-robotic humanoids.
|
||||
#define MOB_CLASS_CONSTRUCT 4 // Silicons, mechanical simple mobs, and FBPs.
|
||||
#define MOB_CLASS_SLIME 8 // Everyone's favorite xenobiology specimen.
|
||||
#define MOB_CLASS_ABERRATION 16 // Weird shit.
|
||||
#define MOB_CLASS_DEMONIC 32 // Cult stuff.
|
||||
#define MOB_CLASS_BOSS 64 // Future megafauna hopefully someday.
|
||||
#define MOB_CLASS_ILLUSION 128 // Fake mobs, e.g. Technomancer illusions.
|
||||
#define MOB_CLASS_NONE 0 // Default value, and used to invert for _ALL.
|
||||
|
||||
#define MOB_CLASS_ALL (MOB_CLASS_ANIMAL|MOB_CLASS_HUMANOID|MOB_CLASS_CONSTRUCT|MOB_CLASS_SLIME|MOB_CLASS_ABERRATION|MOB_CLASS_DEMONIC|MOB_CLASS_BOSS|MOB_CLASS_ILLUSION)
|
||||
#define MOB_CLASS_PLANT 1 // Unused at the moment.
|
||||
#define MOB_CLASS_ANIMAL 2 // Animals and beasts like spiders, saviks, and bears.
|
||||
#define MOB_CLASS_HUMANOID 4 // Non-robotic humanoids, including /simple_mob and /carbon/humans and their alien variants.
|
||||
#define MOB_CLASS_SYNTHETIC 8 // Silicons, mechanical simple mobs, FBPs, and anything else that would pass is_synthetic()
|
||||
#define MOB_CLASS_SLIME 16 // Everyone's favorite xenobiology specimen (and maybe prometheans?).
|
||||
#define MOB_CLASS_ABERRATION 32 // Weird shit.
|
||||
#define MOB_CLASS_DEMONIC 64 // Cult stuff.
|
||||
#define MOB_CLASS_BOSS 128 // Future megafauna hopefully someday.
|
||||
#define MOB_CLASS_ILLUSION 256 // Fake mobs, e.g. Technomancer illusions.
|
||||
|
||||
#define MOB_CLASS_ALL (~MOB_CLASS_NONE)
|
||||
|
||||
// For slime commanding. Higher numbers allow for more actions.
|
||||
#define SLIME_COMMAND_OBEY 1 // When disciplined.
|
||||
|
||||
@@ -21,6 +21,8 @@ SUBSYSTEM_DEF(ai)
|
||||
// var/mob/living/L = currentrun[currentrun.len]
|
||||
var/datum/ai_holder/A = currentrun[currentrun.len]
|
||||
--currentrun.len
|
||||
if(!A || QDELETED(A)) // Doesn't exist or won't exist soon.
|
||||
continue
|
||||
if(times_fired % 4 == 0 && A.holder.stat != DEAD)
|
||||
A.handle_strategicals()
|
||||
if(A.holder.stat != DEAD) // The /TG/ version checks stat twice, presumably in-case processing somehow got the mob killed in that instant.
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
//Returns 1 if the turf is dense, or if there's dense objects on it, unless told to ignore them.
|
||||
/turf/proc/check_density(var/ignore_objs = 0)
|
||||
//Returns 1 if the turf is dense, or if there's dense objects/mobs on it, unless told to ignore them.
|
||||
/turf/proc/check_density(var/ignore_objs = FALSE, var/ignore_mobs = FALSE)
|
||||
if(density)
|
||||
return 1
|
||||
if(!ignore_objs)
|
||||
return TRUE
|
||||
if(!ignore_objs || !ignore_mobs)
|
||||
for(var/atom/movable/stuff in contents)
|
||||
if(stuff.density)
|
||||
return 1
|
||||
return 0
|
||||
if(ignore_objs && isobj(stuff))
|
||||
continue
|
||||
else if(ignore_mobs && isliving(stuff)) // Ghosts aren't dense but keeping this limited to living type will probably save headaches in the future.
|
||||
continue
|
||||
else
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
// Used to distinguish friend from foe.
|
||||
/obj/item/weapon/spell/proc/is_ally(var/mob/living/L)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/datum/ai_holder/simple_mob
|
||||
hostile = TRUE // The majority of simplemobs are hostile.
|
||||
cooperative = TRUE
|
||||
returns_home = TRUE
|
||||
returns_home = FALSE
|
||||
can_flee = FALSE
|
||||
speak_chance = 1 // If the mob's saylist is empty, nothing will happen.
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
// Ranged mobs.
|
||||
|
||||
/datum/ai_holder/simple_mob/ranged
|
||||
ranged = TRUE
|
||||
// ranged = TRUE
|
||||
|
||||
// Tries to not waste ammo.
|
||||
/datum/ai_holder/simple_mob/ranged/careful
|
||||
@@ -37,6 +37,20 @@
|
||||
holder.IMove(get_step_away(holder, A, run_if_this_close))
|
||||
holder.face_atom(A)
|
||||
|
||||
// The electric spider's AI.
|
||||
/datum/ai_holder/simple_mob/ranged/electric_spider
|
||||
|
||||
/datum/ai_holder/simple_mob/ranged/electric_spider/max_range(atom/movable/AM)
|
||||
if(isliving(AM))
|
||||
var/mob/living/L = AM
|
||||
if(L.incapacitated(INCAPACITATION_DISABLED) || L.stat == UNCONSCIOUS) // If our target is stunned, go in for the kill.
|
||||
return 1
|
||||
return ..() // Do ranged if possible otherwise.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Melee mobs.
|
||||
|
||||
@@ -54,6 +68,7 @@
|
||||
/datum/ai_holder/simple_mob/melee/hooligan
|
||||
hostile = FALSE
|
||||
retaliate = TRUE
|
||||
returns_home = TRUE
|
||||
max_home_distance = 12
|
||||
var/random_follow = TRUE // Turn off if you want to bus with crabs.
|
||||
|
||||
@@ -101,44 +116,82 @@
|
||||
if(!O.anchored)
|
||||
return TRUE
|
||||
|
||||
/*
|
||||
// This AI hits something, then runs away for awhile.
|
||||
// It will (almost) always flee if they are uncloaked, AND their target is not stunned.
|
||||
/datum/ai_holder/simple_mob/melee/hit_and_run
|
||||
can_flee = TRUE
|
||||
|
||||
// Used for the 'running' part of hit and run.
|
||||
/datum/ai_holder/simple_mob/melee/hit_and_run/special_flee_check()
|
||||
if(!holder.is_cloaked())
|
||||
if(target && isliving(target))
|
||||
var/mob/living/L = target
|
||||
return !L.incapacitated(INCAPACITATION_DISABLED) // Don't flee if our target is stunned in some form, even if uncloaked. This is so the mob keeps attacking a stunned opponent.
|
||||
return TRUE // We're out in the open, uncloaked, and our target isn't stunned, so lets flee.
|
||||
return FALSE
|
||||
|
||||
|
||||
|
||||
/datum/ai_holder/simple_mob/melee/nurse_spider/list_targets()
|
||||
var/list/targets = ..()
|
||||
|
||||
if(targets.len) // Do regular targeting if there's actual enemies.
|
||||
world << "Returned early."
|
||||
world << "targets was [english_list(targets)]."
|
||||
return targets
|
||||
|
||||
// Otherwise lets target objects to web them.
|
||||
var/static/webbable_types = typecacheof(list(/obj/machinery, /obj/item/, /obj/structure))
|
||||
for(var/WT in typecache_filter_list(range(vision_range, holder), webbable_types))
|
||||
var/obj/O = WT
|
||||
if(!O.anchored && can_see(holder, O, vision_range))
|
||||
targets += WT
|
||||
|
||||
world << "targets was [english_list(targets)]."
|
||||
return targets
|
||||
*/
|
||||
/*
|
||||
/datum/ai_holder/simple_mob/melee/nurse_spider/can_attack(atom/movable/the_target)
|
||||
. = ..()
|
||||
if(!.) // Parent returned FALSE.
|
||||
if(istype(the_target, /obj))
|
||||
var/obj/O = the_target
|
||||
if(!O.anchored)
|
||||
return TRUE
|
||||
*/
|
||||
// This AI isolates people it stuns with its 'leap' attack, by dragging them away.
|
||||
/datum/ai_holder/simple_mob/melee/hunter_spider
|
||||
|
||||
/*
|
||||
. = hearers(vision_range, holder) - src // Remove ourselves to prevent suicidal decisions.
|
||||
|
||||
var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha))
|
||||
/datum/ai_holder/simple_mob/melee/hunter_spider/post_special_attack(mob/living/L)
|
||||
drag_away(L)
|
||||
|
||||
for(var/HM in typecache_filter_list(range(vision_range, holder), hostile_machines))
|
||||
if(can_see(holder, HM, vision_range))
|
||||
. += HM
|
||||
// Called after a successful leap.
|
||||
/datum/ai_holder/simple_mob/melee/hunter_spider/proc/drag_away(mob/living/L)
|
||||
world << "Doing drag_away attack on [L]"
|
||||
if(!istype(L))
|
||||
world << "Invalid type."
|
||||
return FALSE
|
||||
|
||||
// If they didn't get stunned, then don't bother.
|
||||
if(!L.incapacitated(INCAPACITATION_DISABLED))
|
||||
world << "Not incapcitated."
|
||||
return FALSE
|
||||
|
||||
// Grab them.
|
||||
if(!holder.start_pulling(L))
|
||||
world << "Failed to pull."
|
||||
return FALSE
|
||||
|
||||
holder.visible_message(span("danger","\The [holder] starts to drag \the [L] away!"))
|
||||
|
||||
var/list/allies = list()
|
||||
var/list/enemies = list()
|
||||
for(var/mob/living/thing in hearers(vision_range, holder))
|
||||
if(thing == holder || thing == L) // Don't count ourselves or the thing we just started pulling.
|
||||
continue
|
||||
if(holder.IIsAlly(thing))
|
||||
allies += thing
|
||||
else
|
||||
enemies += thing
|
||||
|
||||
// First priority: Move our victim to our friends.
|
||||
if(allies.len)
|
||||
world << "Going to move to ally"
|
||||
give_destination(get_turf(pick(allies)), min_distance = 2, combat = TRUE) // This will switch our stance.
|
||||
|
||||
// Second priority: Move our victim away from their friends.
|
||||
// There's a chance of it derping and pulling towards enemies if there's more than two people.
|
||||
// Preventing that will likely be both a lot of effort for developers and the CPU.
|
||||
else if(enemies.len)
|
||||
world << "Going to move away from enemies"
|
||||
var/mob/living/hostile = pick(enemies)
|
||||
var/turf/move_to = get_turf(hostile)
|
||||
for(var/i = 1 to vision_range) // Move them this many steps away from their friend.
|
||||
move_to = get_step_away(move_to, L, 7)
|
||||
if(move_to)
|
||||
give_destination(move_to, min_distance = 2, combat = TRUE) // This will switch our stance.
|
||||
|
||||
// Third priority: Move our victim SOMEWHERE away from where they were.
|
||||
else
|
||||
world << "Going to move away randomly"
|
||||
var/turf/move_to = get_turf(L)
|
||||
move_to = get_step(move_to, pick(cardinal))
|
||||
for(var/i = 1 to vision_range) // Move them this many steps away from where they were before.
|
||||
move_to = get_step_away(move_to, L, 7)
|
||||
if(move_to)
|
||||
give_destination(move_to, min_distance = 2, combat = TRUE) // This will switch our stance.
|
||||
*/
|
||||
@@ -4,8 +4,8 @@
|
||||
var/firing_lanes = FALSE // If ture, tries to refrain from shooting allies or the wall.
|
||||
var/conserve_ammo = FALSE // If true, the mob will avoid shooting anything that does not have a chance to hit a mob. Requires firing_lanes to be true.
|
||||
|
||||
var/ranged = FALSE // If true, attempts to shoot at the enemy instead of charging at them wildly.
|
||||
var/shoot_range = 5 // How close the mob needs to be to attempt to shoot at the enemy.
|
||||
// var/ranged = FALSE // If true, attempts to shoot at the enemy instead of charging at them wildly.
|
||||
var/shoot_range = 5 // How close the mob needs to be to attempt to shoot at the enemy, if the mob is capable of ranged attacks.
|
||||
var/pointblank = FALSE // If ranged is true, and this is true, people adjacent to the mob will suffer the ranged instead of using a melee attack.
|
||||
|
||||
var/special_attack_prob = 0 // The chance to ATTEMPT a special_attack(). If it fails, it will do a regular attack instead.
|
||||
@@ -54,7 +54,8 @@
|
||||
request_help() // Call our allies.
|
||||
|
||||
// Do a 'special' attack, if one is allowed.
|
||||
if(prob(special_attack_prob) && (distance >= special_attack_min_range) && (distance <= special_attack_max_range))
|
||||
// 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.
|
||||
@@ -68,7 +69,7 @@
|
||||
melee_attack(target)
|
||||
|
||||
// Shoot them.
|
||||
else if(ranged && (distance <= shoot_range) )
|
||||
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.
|
||||
@@ -99,7 +100,10 @@
|
||||
|
||||
// Most mobs probably won't have this defined but we don't care.
|
||||
/datum/ai_holder/proc/special_attack(atom/movable/AM)
|
||||
return holder.ISpecialAttack(AM)
|
||||
. = holder.ISpecialAttack(AM)
|
||||
world << "special_attack result is [.]"
|
||||
if(.)
|
||||
post_special_attack(AM)
|
||||
|
||||
// Called when within striking/shooting distance, however cooldown is not considered.
|
||||
// Override to do things like move in a random step for evasiveness.
|
||||
@@ -107,11 +111,15 @@
|
||||
/datum/ai_holder/proc/on_engagement(atom/A)
|
||||
|
||||
// Called after a successful (IE not on cooldown) ranged attack.
|
||||
// Note that this is not whether the projectile actually hit, just that one was launched.
|
||||
/datum/ai_holder/proc/post_ranged_attack(atom/A)
|
||||
|
||||
// Ditto but for melee.
|
||||
/datum/ai_holder/proc/post_melee_attack(atom/A)
|
||||
|
||||
// And one more for special snowflake attacks.
|
||||
/datum/ai_holder/proc/post_special_attack(atom/A)
|
||||
|
||||
// Used to make sure projectiles will probably hit the target and not the wall or a friend.
|
||||
/datum/ai_holder/proc/test_projectile_safety(atom/movable/AM)
|
||||
var/mob/living/L = check_trajectory(AM, holder) // This isn't always reliable but its better than the previous method.
|
||||
@@ -137,13 +145,17 @@
|
||||
var/distance = get_dist(holder, AM)
|
||||
if(distance <= 1)
|
||||
return TRUE // Can melee.
|
||||
else if(ranged && distance <= shoot_range)
|
||||
else if(holder.ICheckRangedAttack(AM) && distance <= max_range(AM))
|
||||
return TRUE // Can shoot.
|
||||
return FALSE
|
||||
|
||||
// Determines how close the AI will move to its target.
|
||||
/datum/ai_holder/proc/closest_distance(atom/movable/AM)
|
||||
return max(max_range(AM) - 1, 1) // Max range -1 just because we don't want to constantly get kited
|
||||
|
||||
// Can be used to conditionally do a ranged or melee attack.
|
||||
/datum/ai_holder/proc/closest_distance()
|
||||
return ranged ? shoot_range - 1 : 1 // Shoot range -1 just because we don't want to constantly get kited
|
||||
/datum/ai_holder/proc/max_range(atom/movable/AM)
|
||||
return holder.ICheckRangedAttack(AM) ? 7 : 1
|
||||
|
||||
// Goes to the target, to attack them.
|
||||
// Called when in STANCE_APPROACH.
|
||||
@@ -160,18 +172,21 @@
|
||||
ai_log("walk_to_target() : Found new target ([target]).", AI_LOG_INFO)
|
||||
|
||||
// Find out where we're going.
|
||||
var/get_to = closest_distance()
|
||||
var/get_to = closest_distance(target)
|
||||
var/distance = get_dist(holder, target)
|
||||
ai_log("walk_to_target() : get_to is [get_to].", AI_LOG_TRACE)
|
||||
|
||||
// We're here!
|
||||
if(distance <= get_to)
|
||||
// Special case: Our holder has a special attack that is ranged, but normally the holder uses melee.
|
||||
// If that happens, we'll switch to STANCE_FIGHT so they can use it. If the special attack is limited, they'll likely switch back next tick.
|
||||
if(distance <= get_to || holder.ICheckSpecialAttack(target))
|
||||
ai_log("walk_to_target() : Within range.", AI_LOG_INFO)
|
||||
forget_path()
|
||||
set_stance(STANCE_FIGHT)
|
||||
ai_log("walk_to_target() : Exiting.", AI_LOG_DEBUG)
|
||||
return
|
||||
|
||||
|
||||
// Otherwise keep walking.
|
||||
walk_path(target, get_to)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
/datum/ai_holder/proc/shoot_near_turf(turf/targeted_turf)
|
||||
if(!ranged)
|
||||
return // Can't shoot.
|
||||
if(get_dist(holder, targeted_turf) > shoot_range)
|
||||
if(get_dist(holder, targeted_turf) > max_range(targeted_turf))
|
||||
return // Too far to shoot.
|
||||
|
||||
var/turf/T = pick(RANGE_TURFS(2, targeted_turf)) // The turf we're actually gonna shoot at.
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
use_astar = TRUE
|
||||
|
||||
/datum/ai_holder/hostile/ranged
|
||||
ranged = TRUE
|
||||
// ranged = TRUE
|
||||
cooperative = TRUE
|
||||
firing_lanes = TRUE
|
||||
conserve_ammo = TRUE
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
return TRUE
|
||||
|
||||
if(can_flee)
|
||||
if(special_flee_check())
|
||||
return TRUE
|
||||
if(!hostile && !retaliate)
|
||||
return TRUE // We're not hostile and someone attacked us first.
|
||||
if(flee_when_dying && (holder.health / holder.getMaxHealth()) <= dying_threshold)
|
||||
@@ -24,12 +26,17 @@
|
||||
return TRUE // We're fighting something way way stronger then us.
|
||||
return FALSE
|
||||
|
||||
// Override for special fleeing conditionally.
|
||||
/datum/ai_holder/proc/special_flee_check()
|
||||
return FALSE
|
||||
|
||||
/datum/ai_holder/proc/flee_from_target()
|
||||
ai_log("flee_from_target() : Entering.", AI_LOG_DEBUG)
|
||||
|
||||
if(!target || !can_attack(target)) // can_attack() is used since it checks the same things we would need to anyways.
|
||||
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
|
||||
|
||||
|
||||
@@ -5,9 +5,12 @@
|
||||
var/retaliate = FALSE // Attacks whatever struck it first. Mobs will still attack back if this is false but hostile is true.
|
||||
|
||||
var/atom/movable/target = null // The thing (mob or object) we're trying to kill.
|
||||
var/atom/movable/preferred_target = null// If set, and if given the chance, we will always prefer to target this over other options.
|
||||
var/turf/target_last_seen_turf = null // Where the mob last observed the target being, used if the target disappears but the mob wants to keep fighting.
|
||||
|
||||
var/vision_range = 7 // How far the targeting system will look for things to kill. Note that values higher than 7 are 'offscreen' and might be unsporting.
|
||||
var/respect_alpha = TRUE // If true, mobs with a sufficently low alpha will be treated as invisible.
|
||||
var/alpha_vision_threshold = 127 // Targets with an alpha less or equal to this will be considered invisible. Requires above var to be true.
|
||||
|
||||
var/lose_target_time = 0 // world.time when a target was lost.
|
||||
var/lose_target_timeout = 5 SECONDS // How long until a mob 'times out' and stops trying to find the mob that disappeared.
|
||||
@@ -56,7 +59,12 @@
|
||||
// targets -= A
|
||||
if(!targets.len) // We found nothing.
|
||||
return
|
||||
var/chosen_target = pick(targets)
|
||||
|
||||
var/chosen_target
|
||||
if(preferred_target && preferred_target in targets)
|
||||
chosen_target = preferred_target
|
||||
else
|
||||
chosen_target = pick(targets)
|
||||
return chosen_target
|
||||
|
||||
// Step 4, give us our selected target.
|
||||
@@ -87,7 +95,7 @@
|
||||
|
||||
if(isliving(the_target))
|
||||
var/mob/living/L = the_target
|
||||
if(L.stat)
|
||||
if(L.stat == DEAD)
|
||||
return FALSE
|
||||
if(holder.IIsAlly(L))
|
||||
return FALSE
|
||||
@@ -138,10 +146,14 @@
|
||||
ai_log("can_see_target() : There is no target. Exiting.", AI_LOG_WARNING)
|
||||
return FALSE
|
||||
|
||||
if(holder.see_invisible < the_target.invisibility) // Invisible, can't see it, oh well.
|
||||
if(holder.see_invisible < the_target.invisibility) // Real invis.
|
||||
ai_log("can_see_target() : Target ([the_target]) was invisible to holder. Exiting.", AI_LOG_TRACE)
|
||||
return FALSE
|
||||
|
||||
if(respect_alpha && the_target.alpha <= alpha_vision_threshold) // Fake invis.
|
||||
ai_log("can_see_target() : Target ([the_target]) was sufficently transparent to holder and is hidden. Exiting.", AI_LOG_TRACE)
|
||||
return FALSE
|
||||
|
||||
if(get_dist(holder, the_target) > view_range) // Too far away.
|
||||
ai_log("can_see_target() : Target ([the_target]) was too far from holder. Exiting.", AI_LOG_TRACE)
|
||||
return FALSE
|
||||
@@ -195,7 +207,13 @@
|
||||
ai_log("react_to_attack() : Was attacked by [attacker].", AI_LOG_INFO)
|
||||
return give_target(attacker) // Also handles setting the appropiate stance.
|
||||
|
||||
/*
|
||||
if(ai_inactive || stat || M == target_mob) return //Not if we're dead or already hitting them
|
||||
if(M in friends || M.faction == faction) return //I'll overlook it THIS time...
|
||||
*/
|
||||
// Causes targeting to prefer targeting the taunter if possible.
|
||||
// This generally occurs if more than one option is within striking distance, including the taunter.
|
||||
// Otherwise the default filter will prefer the closest target.
|
||||
/datum/ai_holder/proc/receive_taunt(atom/movable/taunter)
|
||||
ai_log("receive_taunt() : Was taunted by [taunter].", AI_LOG_INFO)
|
||||
preferred_target = taunter
|
||||
|
||||
/datum/ai_holder/proc/lose_taunt()
|
||||
ai_log("lose_taunt() : Resetting preferred_target.", AI_LOG_INFO)
|
||||
preferred_target = null
|
||||
@@ -7,20 +7,38 @@
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_mob/IAttack(atom/A)
|
||||
return attack_target(A)
|
||||
if(!canClick()) // Still on cooldown from a "click".
|
||||
return FALSE
|
||||
return attack_target(A) // This will set click cooldown.
|
||||
|
||||
/mob/living/proc/IRangedAttack(atom/A)
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_mob/IRangedAttack(atom/A)
|
||||
if(!canClick()) // Still on cooldown from a "click".
|
||||
return FALSE
|
||||
return shoot_target(A)
|
||||
|
||||
// Test if the AI is allowed to use to attempt a ranged attack.
|
||||
/mob/living/proc/ICheckRangedAttack(atom/A)
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_mob/ICheckRangedAttack(atom/A)
|
||||
return projectiletype ? TRUE : FALSE
|
||||
|
||||
/mob/living/proc/ISpecialAttack(atom/A)
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_mob/ISpecialAttack(atom/A)
|
||||
return special_attack_target(A)
|
||||
|
||||
// Is the AI allowed to attempt to do it?
|
||||
/mob/living/proc/ICheckSpecialAttack(atom/A)
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_mob/ICheckSpecialAttack(atom/A)
|
||||
return can_special_attack(A)
|
||||
|
||||
/mob/living/proc/ISay(message)
|
||||
|
||||
/mob/living/proc/IIsAlly(mob/living/L)
|
||||
|
||||
@@ -176,8 +176,33 @@ note dizziness decrements automatically in the mob's Life() proc.
|
||||
pixel_z = default_pixel_z
|
||||
alpha = initial_alpha
|
||||
|
||||
/atom/movable/proc/do_attack_animation(atom/A)
|
||||
// Similar to attack animations, but in reverse and is longer to act as a telegraph.
|
||||
/atom/movable/proc/do_windup_animation(atom/A, windup_time)
|
||||
var/pixel_x_diff = 0
|
||||
var/pixel_y_diff = 0
|
||||
var/direction = get_dir(src, A)
|
||||
if(direction & NORTH)
|
||||
pixel_y_diff = -8
|
||||
else if(direction & SOUTH)
|
||||
pixel_y_diff = 8
|
||||
|
||||
if(direction & EAST)
|
||||
pixel_x_diff = -8
|
||||
else if(direction & WEST)
|
||||
pixel_x_diff = 8
|
||||
|
||||
var/default_pixel_x = initial(pixel_x)
|
||||
var/default_pixel_y = initial(pixel_y)
|
||||
var/mob/mob = src
|
||||
if(istype(mob))
|
||||
default_pixel_x = mob.default_pixel_x
|
||||
default_pixel_y = mob.default_pixel_y
|
||||
|
||||
animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = windup_time - 2)
|
||||
animate(pixel_x = default_pixel_x, pixel_y = default_pixel_y, time = 2)
|
||||
|
||||
|
||||
/atom/movable/proc/do_attack_animation(atom/A)
|
||||
var/pixel_x_diff = 0
|
||||
var/pixel_y_diff = 0
|
||||
var/direction = get_dir(src, A)
|
||||
|
||||
@@ -42,6 +42,17 @@
|
||||
if(cloaker.active)
|
||||
cloaker.deactivate()
|
||||
|
||||
/mob/living/carbon/human/is_cloaked()
|
||||
if(mind && mind.changeling) // Ling camo.
|
||||
return mind.changeling.cloaked
|
||||
else if(istype(back, /obj/item/weapon/rig)) //Ninja cloak
|
||||
var/obj/item/weapon/rig/suit = back
|
||||
for(var/obj/item/rig_module/stealth_field/cloaker in suit.installed_modules)
|
||||
if(cloaker.active)
|
||||
return TRUE
|
||||
else
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/human/get_ear_protection()
|
||||
var/sum = 0
|
||||
if(istype(l_ear, /obj/item/clothing/ears))
|
||||
|
||||
@@ -675,9 +675,9 @@
|
||||
|
||||
//SA vs SA basically
|
||||
/mob/living/simple_animal/attack_generic(var/mob/attacker)
|
||||
..()
|
||||
if(attacker)
|
||||
react_to_attack(attacker)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/movement_delay()
|
||||
var/tally = 0 //Incase I need to add stuff other than "speed" later
|
||||
|
||||
@@ -1,24 +1,40 @@
|
||||
// Does a melee attack.
|
||||
/mob/living/simple_mob/proc/attack_target(atom/A)
|
||||
set waitfor = FALSE // For attack animations, if they're ever added. Don't want the AI processor to get held up.
|
||||
set waitfor = FALSE // For attack animations. Don't want the AI processor to get held up.
|
||||
|
||||
if(!A.Adjacent(src))
|
||||
return FALSE
|
||||
if(!canClick()) // Still on cooldown from a "click".
|
||||
return FALSE
|
||||
var/turf/their_T = get_turf(A)
|
||||
if(attack_delay)
|
||||
// their_T.color = "#FF0000"
|
||||
melee_pre_animation(A)
|
||||
handle_attack_delay(A) // This will sleep this proc for a bit, which is why waitfor is false.
|
||||
|
||||
// Cooldown testing is done at click code (for players) and interface code (for AI).
|
||||
setClickCooldown(get_attack_speed())
|
||||
|
||||
return do_attack(A)
|
||||
. = do_attack(A, their_T)
|
||||
|
||||
if(attack_delay)
|
||||
melee_post_animation(A)
|
||||
// their_T.color = "#FFFFFF"
|
||||
|
||||
|
||||
|
||||
// This does the actual attack.
|
||||
// This is a seperate proc for the purposes of attack animations.
|
||||
/mob/living/simple_mob/proc/do_attack(atom/A)
|
||||
if(!A.Adjacent(src)) // They could've moved in the meantime.
|
||||
// A is the thing getting attacked, T is the turf A is/was on when attack_target was called.
|
||||
/mob/living/simple_mob/proc/do_attack(atom/A, turf/T)
|
||||
// if(!A.Adjacent(src))
|
||||
if((!(A in T)) || !T.Adjacent(src)) // Most likely we have a slow attack and they dodged it or we somehow got moved.
|
||||
add_attack_logs(src, A, "Animal-attacked (dodged)", admin_notify = FALSE)
|
||||
playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1)
|
||||
return FALSE
|
||||
|
||||
var/damage_to_do = rand(melee_damage_lower, melee_damage_upper)
|
||||
|
||||
damage_to_do = apply_bonus_melee_damage(A, damage_to_do)
|
||||
|
||||
for(var/datum/modifier/M in modifiers)
|
||||
if(!isnull(M.outgoing_melee_damage_percent))
|
||||
damage_to_do *= M.outgoing_melee_damage_percent
|
||||
@@ -36,27 +52,40 @@
|
||||
if(H.check_shields(damage = damage_to_do, damage_source = src, attacker = src, def_zone = null, attack_text = "the attack"))
|
||||
return FALSE // We were blocked.
|
||||
|
||||
if(A.attack_generic(src, damage_to_do, pick(attacktext)) && attack_sound)
|
||||
if(A.attack_generic(src, damage_to_do, pick(attacktext)))
|
||||
apply_melee_effects(A)
|
||||
playsound(src, attack_sound, 75, 1)
|
||||
if(attack_sound)
|
||||
playsound(src, attack_sound, 75, 1)
|
||||
|
||||
return TRUE
|
||||
|
||||
// Override for special effects after a successful attack.
|
||||
// Override for special effects after a successful attack, like injecting poison or stunning the target.
|
||||
/mob/living/simple_mob/proc/apply_melee_effects(atom/A)
|
||||
return
|
||||
|
||||
// Override to modify the amount of damage the mob does conditionally.
|
||||
// This must return the amount of outgoing damage.
|
||||
// Note that this is done before mob modifiers scale the damage.
|
||||
/mob/living/simple_mob/proc/apply_bonus_melee_damage(atom/A, damage_amount)
|
||||
return damage_amount
|
||||
|
||||
//The actual top-level ranged attack proc
|
||||
/mob/living/simple_mob/proc/shoot_target(atom/A)
|
||||
if(!canClick())
|
||||
return FALSE
|
||||
|
||||
set waitfor = FALSE
|
||||
setClickCooldown(get_attack_speed())
|
||||
|
||||
if(attack_delay)
|
||||
ranged_pre_animation(A)
|
||||
handle_attack_delay(A) // This will sleep this proc for a bit, which is why waitfor is false.
|
||||
|
||||
visible_message("<span class='danger'><b>\The [src]</b> fires at \the [A]!</span>")
|
||||
shoot(A, src.loc, src)
|
||||
if(casingtype)
|
||||
new casingtype
|
||||
|
||||
if(attack_delay)
|
||||
ranged_post_animation(A)
|
||||
|
||||
return TRUE
|
||||
|
||||
|
||||
@@ -71,7 +100,73 @@
|
||||
return
|
||||
P.launch(A)
|
||||
|
||||
// if(distance >= special_attack_min_range && distance <= special_attack_max_range)
|
||||
// return TRUE
|
||||
|
||||
//Special attacks, like grenades or blinding spit or whatever
|
||||
// Can we currently do a special attack?
|
||||
/mob/living/simple_mob/proc/can_special_attack(atom/A)
|
||||
// Validity check.
|
||||
if(!istype(A))
|
||||
return FALSE
|
||||
|
||||
// Ability check.
|
||||
if(isnull(special_attack_min_range) || isnull(special_attack_max_range))
|
||||
return FALSE
|
||||
|
||||
// Distance check.
|
||||
var/distance = get_dist(src, A)
|
||||
if(distance < special_attack_min_range || distance > special_attack_max_range)
|
||||
return FALSE
|
||||
|
||||
// Cooldown check.
|
||||
if(!isnull(special_attack_cooldown) && last_special_attack + special_attack_cooldown > world.time)
|
||||
return FALSE
|
||||
|
||||
// Charge check.
|
||||
if(!isnull(special_attack_charges) && special_attack_charges <= 0)
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
|
||||
// Special attacks, like grenades or blinding spit or whatever.
|
||||
// Don't override this, override do_special_attack() for your blinding spit/etc.
|
||||
/mob/living/simple_mob/proc/special_attack_target(atom/A)
|
||||
return FALSE
|
||||
last_special_attack = world.time
|
||||
if(do_special_attack(A))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
// Override this for the actual special attack.
|
||||
/mob/living/simple_mob/proc/do_special_attack(atom/A)
|
||||
return FALSE
|
||||
|
||||
// Sleeps the proc that called it for the correct amount of time.
|
||||
// Also makes sure the AI doesn't do anything stupid in the middle of the delay.
|
||||
/mob/living/simple_mob/proc/handle_attack_delay(atom/A)
|
||||
set_AI_busy(TRUE)
|
||||
|
||||
// Click delay modifiers also affect telegraphing time.
|
||||
// This means berserked enemies will leave less time to dodge.
|
||||
var/true_attack_delay = attack_delay
|
||||
for(var/datum/modifier/M in modifiers)
|
||||
if(!isnull(M.attack_speed_percent))
|
||||
attack_delay *= M.attack_speed_percent
|
||||
|
||||
setClickCooldown(true_attack_delay) // Insurance against a really long attack being longer than default click delay.
|
||||
|
||||
sleep(true_attack_delay)
|
||||
|
||||
set_AI_busy(FALSE)
|
||||
|
||||
|
||||
// Override these four for special custom animations (like the GOLEM).
|
||||
/mob/living/simple_mob/proc/melee_pre_animation(atom/A)
|
||||
do_windup_animation(A, attack_delay)
|
||||
|
||||
/mob/living/simple_mob/proc/melee_post_animation(atom/A)
|
||||
|
||||
/mob/living/simple_mob/proc/ranged_pre_animation(atom/A)
|
||||
do_windup_animation(A, attack_delay) // Semi-placeholder.
|
||||
|
||||
/mob/living/simple_mob/proc/ranged_post_animation(atom/A)
|
||||
@@ -17,9 +17,8 @@
|
||||
custom_emote(1,"[pick(friendly)] [A]!")
|
||||
|
||||
if(I_HURT)
|
||||
if(prob(special_attack_prob))
|
||||
if(special_attack_min_range <= 1)
|
||||
special_attack_target(A)
|
||||
if(can_special_attack(A) && special_attack_target(A))
|
||||
return
|
||||
|
||||
else if(melee_damage_upper == 0 && istype(A,/mob/living))
|
||||
custom_emote(1,"[pick(friendly)] [A]!")
|
||||
@@ -37,10 +36,8 @@
|
||||
|
||||
/mob/living/simple_mob/RangedAttack(var/atom/A)
|
||||
// setClickCooldown(get_attack_speed())
|
||||
var/distance = get_dist(src, A)
|
||||
|
||||
if(prob(special_attack_prob) && (distance >= special_attack_min_range) && (distance <= special_attack_max_range))
|
||||
special_attack_target()
|
||||
if(can_special_attack(A) && special_attack_target(A))
|
||||
return
|
||||
|
||||
if(projectiletype)
|
||||
|
||||
@@ -83,17 +83,23 @@
|
||||
var/melee_damage_upper = 6 // Upper bound of randomized melee damage
|
||||
var/list/attacktext = list("attacked") // "You are [attacktext] by the mob!"
|
||||
var/list/friendly = list("nuzzles") // "The mob [friendly] the person."
|
||||
var/attack_sound = null // Sound to play when I attack
|
||||
var/melee_miss_chance = 15 // percent chance to miss a melee attack.
|
||||
var/attack_sound = null // Sound to play when I attack
|
||||
var/melee_miss_chance = 0 // percent chance to miss a melee attack.
|
||||
var/attack_armor_type = "melee" // What armor does this check?
|
||||
var/attack_armor_pen = 0 // How much armor pen this attack has.
|
||||
var/attack_sharp = 0 // Is the attack sharp?
|
||||
var/attack_edge = 0 // Does the attack have an edge?
|
||||
var/attack_sharp = FALSE // Is the attack sharp?
|
||||
var/attack_edge = FALSE // Does the attack have an edge?
|
||||
var/attack_delay = null // If set, the mob will do a windup animation and can miss if the target moves out of the way.
|
||||
|
||||
//Special attacks
|
||||
var/special_attack_prob = 0 // Chance of the mob doing a special attack (0 for never)
|
||||
var/special_attack_min_range = 0 // Min range to perform the special attacks from
|
||||
var/special_attack_max_range = 0 // Max range to perform special attacks from
|
||||
// var/special_attack_prob = 0 // The chance to ATTEMPT a special_attack_target(). If it fails, it will do a regular attack instead.
|
||||
// This is commented out to ease the AI attack logic by being (a bit more) determanistic.
|
||||
// You should instead limit special attacks using the below vars instead.
|
||||
var/special_attack_min_range = null // The minimum distance required for an attempt to be made.
|
||||
var/special_attack_max_range = null // The maximum for an attempt.
|
||||
var/special_attack_charges = null // If set, special attacks will work off of a charge system, and won't be usable if all charges are expended. Good for grenades.
|
||||
var/special_attack_cooldown = null // If set, special attacks will have a cooldown between uses.
|
||||
var/last_special_attack = null // world.time when a special attack occured last, for cooldown calculations.
|
||||
|
||||
//Damage resistances
|
||||
var/grab_resist = 0 // Chance for a grab attempt to fail. Note that this is not a true resist and is just a prob() of failure.
|
||||
@@ -151,7 +157,7 @@
|
||||
//Client attached
|
||||
/mob/living/simple_mob/Login()
|
||||
. = ..()
|
||||
to_chat(src,"<b>You are \the [src]. [player_msg]</b>")
|
||||
to_chat(src,"<b>You are \the [src].</b> [player_msg]")
|
||||
|
||||
|
||||
/mob/living/simple_mob/emote(var/act, var/type, var/desc)
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// Carriers are not too dangerous on their own, but they create more spiders when dying.
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/carrier
|
||||
desc = "Furry, beige, and red, it makes you shudder to look at it. This one has luminous green eyes."
|
||||
icon_state = "carrier"
|
||||
icon_living = "carrier"
|
||||
icon_dead = "carrier_dead"
|
||||
|
||||
maxHealth = 100
|
||||
health = 100
|
||||
|
||||
melee_damage_lower = 8
|
||||
melee_damage_upper = 25
|
||||
|
||||
poison_per_bite = 3
|
||||
poison_type = "chloralhydrate"
|
||||
|
||||
movement_cooldown = 5
|
||||
|
||||
player_msg = "Upon dying, you will release a swarm of spiderlings or young hunter spiders.<br>\
|
||||
If a spider emerges, you will be placed in control of it."
|
||||
|
||||
var/spiderling_count = 0
|
||||
var/spiderling_type = /obj/effect/spider/spiderling
|
||||
var/swarmling_type = /mob/living/simple_mob/animal/giant_spider/hunter
|
||||
var/swarmling_faction = "spiders"
|
||||
var/swarmling_prob = 10 // Odds that a spiderling will be a swarmling instead.
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/carrier/initialize()
|
||||
spiderling_count = rand(5, 10)
|
||||
adjust_scale(1.2)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/carrier/death()
|
||||
visible_message(span("warning", "\The [src]'s abdomen splits as it rolls over, spiderlings crawling from the wound.") )
|
||||
spawn(1)
|
||||
var/list/new_spiders = list()
|
||||
for(var/i = 1 to spiderling_count)
|
||||
if(prob(swarmling_prob) && src)
|
||||
var/mob/living/simple_mob/animal/giant_spider/swarmling = new swarmling_type(src.loc)
|
||||
var/swarm_health = Floor(swarmling.maxHealth * 0.4)
|
||||
var/swarm_dam_lower = Floor(melee_damage_lower * 0.4)
|
||||
var/swarm_dam_upper = Floor(melee_damage_upper * 0.4)
|
||||
swarmling.name = "spiderling"
|
||||
swarmling.maxHealth = swarm_health
|
||||
swarmling.health = swarm_health
|
||||
swarmling.melee_damage_lower = swarm_dam_lower
|
||||
swarmling.melee_damage_upper = swarm_dam_upper
|
||||
swarmling.faction = swarmling_faction
|
||||
swarmling.adjust_scale(0.75)
|
||||
new_spiders += swarmling
|
||||
else if(src)
|
||||
var/obj/effect/spider/spiderling/child = new spiderling_type(src.loc)
|
||||
child.skitter()
|
||||
else // We might've gibbed or got deleted.
|
||||
break
|
||||
// Transfer our player to their new body, if RNG provided one.
|
||||
if(new_spiders.len && client)
|
||||
var/mob/living/simple_mob/animal/giant_spider/new_body = pick(new_spiders)
|
||||
new_body.key = src.key
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/carrier/recursive
|
||||
desc = "Furry, beige, and red, it makes you shudder to look at it. This one has luminous green eyes. \
|
||||
You have a distinctly <font face='comic sans ms'>bad</font> feeling about this."
|
||||
|
||||
swarmling_type = /mob/living/simple_animal/hostile/giant_spider/carrier/recursive
|
||||
@@ -0,0 +1,33 @@
|
||||
// Electric spiders fire taser-like beams at their enemies.
|
||||
// TODO: AI
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/electric
|
||||
desc = "Spined and yellow, it makes you shudder to look at it. This one has flickering gold eyes."
|
||||
icon_state = "spark"
|
||||
icon_living = "spark"
|
||||
icon_dead = "spark_dead"
|
||||
|
||||
maxHealth = 210
|
||||
health = 210
|
||||
|
||||
taser_kill = 0 //It -is- the taser.
|
||||
|
||||
base_attack_cooldown = 10
|
||||
projectilesound = 'sound/weapons/taser2.ogg'
|
||||
projectiletype = /obj/item/projectile/beam/stun/electric_spider
|
||||
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 25
|
||||
|
||||
poison_chance = 15
|
||||
poison_per_bite = 3
|
||||
poison_type = "stimm"
|
||||
|
||||
player_msg = "You can fire a taser-like ranged attack by clicking on an enemy or tile at a distance."
|
||||
|
||||
ai_holder_type = /datum/ai_holder/simple_mob/ranged/electric_spider
|
||||
|
||||
|
||||
/obj/item/projectile/beam/stun/electric_spider
|
||||
name = "stun beam"
|
||||
agony = 20
|
||||
@@ -0,0 +1,18 @@
|
||||
// Frost spiders inject cryotoxin, slowing people down (which is very bad if trying to run from spiders).
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/frost
|
||||
desc = "Icy and blue, it makes you shudder to look at it. This one has brilliant blue eyes."
|
||||
icon_state = "frost"
|
||||
icon_living = "frost"
|
||||
icon_dead = "frost_dead"
|
||||
|
||||
maxHealth = 175
|
||||
health = 175
|
||||
|
||||
poison_per_bite = 5
|
||||
poison_type = "cryotoxin"
|
||||
|
||||
// Sif variant with a somewhat different desc.
|
||||
/mob/living/simple_mob/animal/giant_spider/frost/sif
|
||||
desc = "Icy and blue, it makes you shudder to look at it. This one has brilliant blue eyes. \
|
||||
It isn't native to Sif."
|
||||
+2
-3
@@ -30,9 +30,11 @@
|
||||
melee_damage_upper = 30
|
||||
attack_sharp = 1
|
||||
attack_edge = 1
|
||||
attack_sound = 'sound/weapons/bite.ogg'
|
||||
|
||||
heat_damage_per_tick = 20
|
||||
cold_damage_per_tick = 20
|
||||
minbodytemp = 175 // So they can all survive Sif without having to be classed under /sif subtype.
|
||||
|
||||
speak_emote = list("chitters")
|
||||
|
||||
@@ -59,6 +61,3 @@
|
||||
to_chat(L, "<span class='warning'>You feel a tiny prick.</span>")
|
||||
L.reagents.add_reagent(poison_type, poison_per_bite)
|
||||
|
||||
// Subtype
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// Hunters are fast, fragile, and possess a leaping attack.
|
||||
// The leaping attack is somewhat telegraphed and can be dodged if one is paying attention.
|
||||
// The AI would've followed up after a successful leap with dragging the downed victim away, but the dragging code was too janky.
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/hunter
|
||||
desc = "Furry and black, it makes you shudder to look at it. This one has sparkling purple eyes."
|
||||
icon_state = "hunter"
|
||||
icon_living = "hunter"
|
||||
icon_dead = "hunter_dead"
|
||||
|
||||
maxHealth = 120
|
||||
health = 120
|
||||
|
||||
poison_per_bite = 5
|
||||
|
||||
movement_cooldown = 0 // Hunters are FAST.
|
||||
|
||||
ai_holder_type = /datum/ai_holder/simple_mob/melee/hunter_spider
|
||||
|
||||
player_msg = "You are very fast, and <b>can perform a leaping attack</b> by clicking on someone from a short distance away.<br>\
|
||||
If the leap succeeds, the target will be knocked down briefly and you will be on top of them.<br>\
|
||||
Note that there is a short delay before you leap!"
|
||||
|
||||
// Leaping is a special attack, so these values determine when leap can happen.
|
||||
// Leaping won't occur if its on cooldown.
|
||||
special_attack_min_range = 2
|
||||
special_attack_max_range = 4
|
||||
special_attack_cooldown = 10 SECONDS
|
||||
|
||||
var/leap_warmup = 1 SECOND // How long the leap telegraphing is.
|
||||
|
||||
// The actual leaping attack.
|
||||
/mob/living/simple_mob/animal/giant_spider/hunter/do_special_attack(atom/A)
|
||||
set waitfor = FALSE
|
||||
set_AI_busy(TRUE)
|
||||
|
||||
// Telegraph, since getting stunned suddenly feels bad.
|
||||
do_windup_animation(A, leap_warmup)
|
||||
sleep(leap_warmup) // For the telegraphing.
|
||||
|
||||
// Do the actual leap.
|
||||
status_flags |= LEAPING // Lets us pass over everything.
|
||||
visible_message(span("danger","\The [src] leaps at \the [A]!"))
|
||||
throw_at(get_step(get_turf(A), get_turf(src)), special_attack_max_range+1, 1, src)
|
||||
playsound(src, 'sound/weapons/spiderlunge.ogg', 75, 1)
|
||||
|
||||
sleep(5) // For the throw to complete. It won't hold up the AI ticker due to waitfor being false.
|
||||
|
||||
if(status_flags & LEAPING)
|
||||
status_flags &= ~LEAPING // Revert special passage ability.
|
||||
|
||||
var/turf/T = get_turf(src) // Where we landed. This might be different than A's turf.
|
||||
|
||||
. = FALSE
|
||||
|
||||
// Now for the stun.
|
||||
var/mob/living/victim = null
|
||||
for(var/mob/living/L in T) // So player-controlled spiders only need to click the tile to stun them.
|
||||
if(L == src)
|
||||
continue
|
||||
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
if(H.check_shields(damage = 0, damage_source = src, attacker = src, def_zone = null, attack_text = "the leap"))
|
||||
continue // We were blocked.
|
||||
|
||||
victim = L
|
||||
break
|
||||
|
||||
if(victim)
|
||||
victim.Weaken(2)
|
||||
victim.visible_message(span("danger","\The [src] knocks down \the [victim]!"))
|
||||
to_chat(victim, span("critical", "\The [src] jumps on you!"))
|
||||
. = TRUE
|
||||
|
||||
set_AI_busy(FALSE)
|
||||
|
||||
// var/obj/item/weapon/grab/G = new(src, victim)
|
||||
// put_in_active_hand(G)
|
||||
|
||||
// G.synch()
|
||||
// G.affecting = victim
|
||||
// victim.LAssailant = src
|
||||
|
||||
// visible_message("<span class='warning'>\The [src] seizes \the [victim] aggressively!</span>")
|
||||
// do_attack_animation(victim)
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// Lurkers are somewhat similar to Hunters, however the big difference is that Lurkers have an imperfect cloak.
|
||||
// Their AI will try to do hit and run tactics, striking the enemy when its "cloaked", for bonus damage and a stun.
|
||||
// They keep attacking until the stun ends, then retreat to cloak again and repeat the cycle.
|
||||
// Hitting the spider before it does its ambush attack will break the cloak and make the spider flee.
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/lurker
|
||||
desc = "Translucent and white, it makes you shudder to look at it. This one has incandescent red eyes."
|
||||
icon_state = "lurker"
|
||||
icon_living = "lurker"
|
||||
icon_dead = "lurker_dead"
|
||||
|
||||
maxHealth = 100
|
||||
health = 100
|
||||
|
||||
poison_per_bite = 5
|
||||
|
||||
movement_cooldown = 5
|
||||
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 10
|
||||
poison_chance = 30
|
||||
poison_type = "cryptobiolin"
|
||||
poison_per_bite = 1
|
||||
|
||||
player_msg = "You have an imperfect, but automatic stealth. If you attack something while 'hidden', then \
|
||||
you will do bonus damage, stun the target, and unstealth for a period of time.<br>\
|
||||
Getting attacked will also break your stealth."
|
||||
|
||||
ai_holder_type = /datum/ai_holder/simple_mob/melee/hit_and_run
|
||||
|
||||
var/cloaked = FALSE
|
||||
var/cloaked_alpha = 45 // Lower = Harder to see.
|
||||
var/cloaked_bonus_damage = 30 // This is added on top of the normal melee damage.
|
||||
var/cloaked_weaken_amount = 3 // How long to stun for.
|
||||
var/cloak_cooldown = 10 SECONDS // Amount of time needed to re-cloak after losing it.
|
||||
var/last_uncloak = 0 // world.time
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/lurker/proc/cloak()
|
||||
if(cloaked)
|
||||
return
|
||||
animate(src, alpha = cloaked_alpha, time = 1 SECOND)
|
||||
cloaked = TRUE
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/lurker/proc/uncloak()
|
||||
last_uncloak = world.time // This is assigned even if it isn't cloaked already, to 'reset' the timer if the spider is continously getting attacked.
|
||||
if(!cloaked)
|
||||
return
|
||||
animate(src, alpha = initial(alpha), time = 1 SECOND)
|
||||
cloaked = FALSE
|
||||
|
||||
|
||||
// Check if cloaking if possible.
|
||||
/mob/living/simple_mob/animal/giant_spider/lurker/proc/can_cloak()
|
||||
if(stat)
|
||||
return FALSE
|
||||
if(last_uncloak + cloak_cooldown > world.time)
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
|
||||
// Called by things that break cloaks, like Technomancer wards.
|
||||
/mob/living/simple_mob/animal/giant_spider/lurker/break_cloak()
|
||||
uncloak()
|
||||
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/lurker/is_cloaked()
|
||||
return cloaked
|
||||
|
||||
|
||||
// Cloaks the spider automatically, if possible.
|
||||
/mob/living/simple_mob/animal/giant_spider/lurker/handle_special()
|
||||
if(!cloaked && can_cloak())
|
||||
cloak()
|
||||
|
||||
|
||||
// Applies bonus base damage if cloaked.
|
||||
/mob/living/simple_mob/animal/giant_spider/lurker/apply_bonus_melee_damage(atom/A, damage_amount)
|
||||
if(cloaked)
|
||||
return damage_amount + cloaked_bonus_damage
|
||||
return ..()
|
||||
|
||||
// Applies stun, then uncloaks.
|
||||
/mob/living/simple_mob/animal/giant_spider/lurker/apply_melee_effects(atom/A)
|
||||
if(cloaked)
|
||||
if(isliving(A))
|
||||
var/mob/living/L = A
|
||||
L.Weaken(cloaked_weaken_amount)
|
||||
to_chat(L, span("danger", "\The [src] ambushes you!"))
|
||||
playsound(L, 'sound/weapons/spiderlunge.ogg', 75, 1)
|
||||
uncloak()
|
||||
..() // For the poison.
|
||||
|
||||
// Force uncloaking if attacked.
|
||||
/mob/living/simple_mob/animal/giant_spider/lurker/bullet_act(obj/item/projectile/P)
|
||||
. = ..()
|
||||
break_cloak()
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/lurker/hit_with_weapon(obj/item/O, mob/living/user, effective_force, hit_zone)
|
||||
. = ..()
|
||||
break_cloak()
|
||||
@@ -0,0 +1,21 @@
|
||||
// Pepper spiders inject condensed capsaicin into their victims.
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/pepper
|
||||
desc = "Red and brown, it makes you shudder to look at it. This one has glinting red eyes."
|
||||
icon_state = "pepper"
|
||||
icon_living = "pepper"
|
||||
icon_dead = "pepper_dead"
|
||||
|
||||
maxHealth = 210
|
||||
health = 210
|
||||
|
||||
melee_damage_lower = 8
|
||||
melee_damage_upper = 15
|
||||
|
||||
poison_chance = 20
|
||||
poison_per_bite = 5
|
||||
poison_type = "condensedcapsaicin_v"
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/pepper/initialize()
|
||||
adjust_scale(1.1)
|
||||
return ..()
|
||||
@@ -0,0 +1,56 @@
|
||||
// Phorogenic spiders explode when they die.
|
||||
// You really shouldn't melee them.
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/phorogenic
|
||||
desc = "Crystalline and purple, it makes you shudder to look at it. This one has haunting purple eyes."
|
||||
icon_state = "phoron"
|
||||
icon_living = "phoron"
|
||||
icon_dead = "phoron_dead"
|
||||
|
||||
maxHealth = 225
|
||||
health = 225
|
||||
taser_kill = FALSE //You will need more than a peashooter to kill the juggernaut.
|
||||
|
||||
melee_damage_lower = 25
|
||||
melee_damage_upper = 40
|
||||
attack_armor_pen = 15
|
||||
|
||||
movement_cooldown = 15
|
||||
|
||||
poison_chance = 30
|
||||
poison_per_bite = 0.5
|
||||
poison_type = "phoron"
|
||||
|
||||
var/exploded = FALSE
|
||||
var/explosion_dev_range = 1
|
||||
var/explosion_heavy_range = 2
|
||||
var/explosion_light_range = 4
|
||||
var/explosion_flash_range = 6 // This doesn't do anything iirc.
|
||||
|
||||
var/explosion_delay_lower = 1 SECOND // Lower bound for explosion delay.
|
||||
var/explosion_delay_upper = 2 SECONDS // Upper bound.
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/phorogenic/initialize()
|
||||
adjust_scale(1.25)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/phorogenic/death()
|
||||
visible_message(span("critical", "\The [src]'s body begins to rupture!"))
|
||||
var/delay = rand(explosion_delay_lower, explosion_delay_upper)
|
||||
spawn(0)
|
||||
// Flash black and red as a warning.
|
||||
for(var/i = 1 to delay)
|
||||
if(i % 2 == 0)
|
||||
color = "#000000"
|
||||
else
|
||||
color = "#FF0000"
|
||||
sleep(1)
|
||||
|
||||
spawn(delay)
|
||||
// The actual boom.
|
||||
if(src && !exploded)
|
||||
visible_message(span("danger", "\The [src]'s body detonates!"))
|
||||
exploded = TRUE
|
||||
explosion(src.loc, explosion_dev_range, explosion_heavy_range, explosion_light_range, explosion_flash_range)
|
||||
return ..()
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Thermic spiders inject a special variant of thermite that burns someone from the inside.
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/thermic
|
||||
desc = "Mirage-cloaked and orange, it makes you shudder to look at it. This one has simmering orange eyes."
|
||||
icon_state = "pit"
|
||||
icon_living = "pit"
|
||||
icon_dead = "pit_dead"
|
||||
|
||||
maxHealth = 175
|
||||
health = 175
|
||||
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 25
|
||||
|
||||
poison_chance = 30
|
||||
poison_per_bite = 1
|
||||
poison_type = "thermite_v"
|
||||
@@ -0,0 +1,208 @@
|
||||
// Tunnelers have a special ability that allows them to charge at an enemy by tunneling towards them.
|
||||
// Any mobs inbetween the tunneler's path and the target will be stunned if the tunneler hits them.
|
||||
// The target will suffer a stun as well, if the tunneler hits them at the end. A successful hit will stop the tunneler.
|
||||
// If the target moves fast enough, the tunneler can miss, causing it to overshoot.
|
||||
// If the tunneler hits a solid wall, the tunneler will suffer a stun.
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/tunneler
|
||||
desc = "Sandy and brown, it makes you shudder to look at it. This one has glittering yellow eyes."
|
||||
icon_state = "tunneler"
|
||||
icon_living = "tunneler"
|
||||
icon_dead = "tunneler_dead"
|
||||
|
||||
maxHealth = 120
|
||||
health = 120
|
||||
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 20
|
||||
|
||||
poison_chance = 15
|
||||
poison_per_bite = 3
|
||||
poison_type = "serotrotium_v"
|
||||
|
||||
// ai_holder_type = /datum/ai_holder/simple_mob/melee/tunneler
|
||||
|
||||
player_msg = "You <b>can perform a tunneling attack</b> by clicking on someone from a distance while on natural terrain.<br>\
|
||||
There is a noticable travel delay as you tunnel towards the tile the target was at when you started the tunneling attack.<br>\
|
||||
Any entities inbetween you and the targeted tile will be stunned for a brief period of time.<br>\
|
||||
Whatever is on the targeted tile when you arrive will suffer a potent stun.<br>\
|
||||
If nothing is on the targeted tile, you will overshoot and keep going for a few more tiles.<br>\
|
||||
If you hit a wall or other solid structure during that time, you will suffer a lengthy stun."
|
||||
|
||||
// Tunneling is a special attack, similar to the hunter's Leap.
|
||||
special_attack_min_range = 2
|
||||
special_attack_max_range = 6
|
||||
special_attack_cooldown = 10 SECONDS
|
||||
|
||||
var/tunnel_warning = 0.5 SECONDS // How long the dig telegraphing is.
|
||||
var/tunnel_tile_speed = 2 // How long to wait between each tile. Higher numbers result in an easier to dodge tunnel attack.
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/tunneler/frequent
|
||||
special_attack_cooldown = 5 SECONDS
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/tunneler/fast
|
||||
tunnel_tile_speed = 1
|
||||
|
||||
/mob/living/simple_mob/animal/giant_spider/tunneler/do_special_attack(atom/A)
|
||||
set waitfor = FALSE
|
||||
set_AI_busy(TRUE)
|
||||
|
||||
// Save where we're gonna go soon.
|
||||
var/turf/destination = get_turf(A)
|
||||
var/turf/starting_turf = get_turf(src)
|
||||
|
||||
// Telegraph to give a small window to dodge if really close.
|
||||
do_windup_animation(A, tunnel_warning)
|
||||
sleep(tunnel_warning) // For the telegraphing.
|
||||
|
||||
// Do the dig!
|
||||
visible_message(span("danger","\The [src] tunnels towards \the [A]!"))
|
||||
|
||||
if(handle_tunnel(destination) == FALSE)
|
||||
set_AI_busy(FALSE)
|
||||
return FALSE
|
||||
|
||||
// Did we make it?
|
||||
if(!(src in destination))
|
||||
set_AI_busy(FALSE)
|
||||
return FALSE
|
||||
|
||||
var/overshoot = TRUE
|
||||
|
||||
// Test if something is at destination.
|
||||
for(var/mob/living/L in destination)
|
||||
if(L == src)
|
||||
continue
|
||||
|
||||
visible_message(span("danger","\The [src] erupts from underneath, and hits \the [L]!"))
|
||||
L.Weaken(3)
|
||||
overshoot = FALSE
|
||||
|
||||
if(!overshoot) // We hit the target, or something, at destination, so we're done.
|
||||
set_AI_busy(FALSE)
|
||||
return TRUE
|
||||
|
||||
// Otherwise we need to keep going.
|
||||
to_chat(src, span("warning", "You overshoot your target!"))
|
||||
playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1)
|
||||
var/dir_to_go = get_dir(starting_turf, destination)
|
||||
for(var/i = 1 to rand(2, 4))
|
||||
destination = get_step(destination, dir_to_go)
|
||||
|
||||
if(handle_tunnel(destination) == FALSE)
|
||||
set_AI_busy(FALSE)
|
||||
return FALSE
|
||||
|
||||
set_AI_busy(FALSE)
|
||||
return FALSE
|
||||
|
||||
|
||||
|
||||
// Does the tunnel movement, stuns enemies, etc.
|
||||
/mob/living/simple_mob/animal/giant_spider/tunneler/proc/handle_tunnel(turf/destination)
|
||||
var/turf/T = get_turf(src) // Hold our current tile.
|
||||
|
||||
// Regular tunnel loop.
|
||||
for(var/i = 1 to get_dist(src, destination))
|
||||
if(stat)
|
||||
return FALSE // We died or got knocked out on the way.
|
||||
if(loc == destination)
|
||||
break // We somehow got there early.
|
||||
|
||||
// Update T.
|
||||
T = get_step(src, get_dir(src, destination))
|
||||
if(T.check_density(ignore_mobs = TRUE))
|
||||
to_chat(src, span("critical", "You hit something really solid!"))
|
||||
playsound(src, "punch", 75, 1)
|
||||
Weaken(5)
|
||||
return FALSE // Hit a wall.
|
||||
|
||||
// Stun anyone in our way.
|
||||
for(var/mob/living/L in T)
|
||||
L.Weaken(2)
|
||||
|
||||
// Get into the tile.
|
||||
forceMove(T)
|
||||
|
||||
// Visuals and sound.
|
||||
new /obj/item/weapon/ore/glass(T)
|
||||
playsound(src, 'sound/effects/break_stone.ogg', 75, 1)
|
||||
sleep(tunnel_tile_speed)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
// The actual leaping attack.
|
||||
/mob/living/simple_mob/animal/giant_spider/hunter/do_special_attack(atom/A)
|
||||
set waitfor = FALSE
|
||||
set_AI_busy(TRUE)
|
||||
|
||||
// Telegraph, since getting stunned suddenly feels bad.
|
||||
do_windup_animation(A, leap_warmup)
|
||||
sleep(leap_warmup) // For the telegraphing.
|
||||
|
||||
// Do the actual leap.
|
||||
status_flags |= LEAPING // Lets us pass over everything.
|
||||
visible_message(span("danger","\The [src] leaps at \the [A]!"))
|
||||
throw_at(get_step(get_turf(A), get_turf(src)), special_attack_max_range+1, 1, src)
|
||||
playsound(src, 'sound/weapons/spiderlunge.ogg', 75, 1)
|
||||
|
||||
sleep(5) // For the throw to complete. It won't hold up the AI ticker due to waitfor being false.
|
||||
|
||||
if(status_flags & LEAPING)
|
||||
status_flags &= ~LEAPING // Revert special passage ability.
|
||||
|
||||
var/turf/T = get_turf(src) // Where we landed. This might be different than A's turf.
|
||||
|
||||
. = FALSE
|
||||
|
||||
// Now for the stun.
|
||||
var/mob/living/victim = null
|
||||
for(var/mob/living/L in T) // So player-controlled spiders only need to click the tile to stun them.
|
||||
if(L == src)
|
||||
continue
|
||||
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
if(H.check_shields(damage = 0, damage_source = src, attacker = src, def_zone = null, attack_text = "the leap"))
|
||||
continue // We were blocked.
|
||||
|
||||
victim = L
|
||||
break
|
||||
|
||||
if(victim)
|
||||
victim.Weaken(2)
|
||||
victim.visible_message(span("danger","\The [src] knocks down \the [victim]!"))
|
||||
to_chat(victim, span("critical", "\The [src] jumps on you!"))
|
||||
. = TRUE
|
||||
|
||||
set_AI_busy(FALSE)
|
||||
*/
|
||||
|
||||
/*
|
||||
/mob/living/simple_animal/hostile/giant_spider/tunneler
|
||||
desc = "Sandy and brown, it makes you shudder to look at it. This one has glittering yellow eyes."
|
||||
icon_state = "tunneler"
|
||||
icon_living = "tunneler"
|
||||
icon_dead = "tunneler_dead"
|
||||
|
||||
maxHealth = 120
|
||||
health = 120
|
||||
move_to_delay = 4
|
||||
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 20
|
||||
|
||||
poison_chance = 15
|
||||
poison_per_bite = 3
|
||||
poison_type = "serotrotium_v"
|
||||
|
||||
/mob/living/simple_animal/hostile/giant_spider/tunneler/death()
|
||||
spawn(1)
|
||||
for(var/I = 1 to rand(3,6))
|
||||
if(src)
|
||||
new/obj/item/weapon/ore/glass(src.loc)
|
||||
else
|
||||
break
|
||||
return ..()
|
||||
*/
|
||||
@@ -3,11 +3,16 @@
|
||||
whether the people want them to or not, and sometimes causing vandalism by accident.
|
||||
They're pretty strong and have strong melee armor, but won't attack first.
|
||||
They unknowingly play a role in keeping the shoreline fairly safe, by killing whatever would attack other people.
|
||||
|
||||
They also have a slow, but very strong attack that is telegraphed. If it hits, it will briefly stun whatever got hit
|
||||
and inflict a very large chunk of damage. If the thing was already stunned, the crab will 'throw' them away, to
|
||||
hopefully prevent chainstuns forever.
|
||||
*/
|
||||
|
||||
/mob/living/simple_mob/animal/sif/hooligan_crab
|
||||
name = "hooligan crab"
|
||||
desc = "A large, hard-shelled crustacean. This one is mostly grey."
|
||||
desc = "A large, hard-shelled crustacean. This one is mostly grey. \
|
||||
You probably shouldn't mess with it."
|
||||
icon_state = "sif_crab"
|
||||
icon_living = "sif_crab"
|
||||
icon_dead = "sif_crab_dead"
|
||||
@@ -19,6 +24,7 @@
|
||||
health = 200
|
||||
movement_cooldown = 10
|
||||
movement_sound = 'sound/weapons/heavysmash.ogg'
|
||||
taser_kill = 0
|
||||
armor = list(
|
||||
"melee" = 40,
|
||||
"bullet" = 20,
|
||||
@@ -45,6 +51,7 @@
|
||||
attack_armor_pen = 35
|
||||
attack_sharp = 1
|
||||
attack_edge = 1
|
||||
attack_delay = 1 SECOND
|
||||
|
||||
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
|
||||
response_help = "pets"
|
||||
@@ -57,3 +64,19 @@
|
||||
ai_holder_type = /datum/ai_holder/simple_mob/melee/hooligan
|
||||
say_list_type = /datum/say_list/crab
|
||||
|
||||
var/weaken_amount = 2 // Be careful with this number. High values will equal a permastun.
|
||||
|
||||
// Stuns the thing that got hit briefly.
|
||||
/mob/living/simple_mob/animal/sif/hooligan_crab/apply_melee_effects(atom/A)
|
||||
if(isliving(A))
|
||||
var/mob/living/L = A
|
||||
var/was_stunned = L.incapacitated(INCAPACITATION_DISABLED)
|
||||
L.Weaken(weaken_amount)
|
||||
|
||||
playsound(L, 'sound/effects/break_stone.ogg', 75, 1)
|
||||
if(was_stunned) // Try to prevent chain-stuns by having them thrown.
|
||||
var/throwdir = get_dir(src, L)
|
||||
L.throw_at(get_edge_target_turf(L, throwdir), 5, 1, src)
|
||||
visible_message(span("danger", "\The [src] hurls \the [L] away!"))
|
||||
else
|
||||
visible_message(span("danger", "\The [src] crushes \the [L]!"))
|
||||
@@ -1,6 +1,5 @@
|
||||
/mob/living/simple_mob/mechanical
|
||||
mob_class = MOB_CLASS_CONSTRUCT
|
||||
|
||||
mob_class = MOB_CLASS_SYNTHETIC
|
||||
min_oxy = 0
|
||||
max_oxy = 0
|
||||
min_tox = 0
|
||||
|
||||
@@ -54,6 +54,9 @@ proc/isdeaf(A)
|
||||
/mob/proc/break_cloak()
|
||||
return
|
||||
|
||||
/mob/proc/is_cloaked()
|
||||
return FALSE
|
||||
|
||||
proc/hasorgans(A) // Fucking really??
|
||||
return ishuman(A)
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 391 KiB After Width: | Height: | Size: 408 KiB |
+10
-1
@@ -1946,8 +1946,17 @@
|
||||
#include "code\modules\mob\living\simple_mob\simple_hud.dm"
|
||||
#include "code\modules\mob\living\simple_mob\simple_mob.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\animal.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\carrier.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\electric.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\frost.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\giant_spider.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\hunter.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\lurker.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\nurse.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\pepper.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\phorogenic.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\thermic.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\tunneler.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\hooligan_crab.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\sif.dm"
|
||||
#include "code\modules\mob\living\simple_mob\subtypes\mechanical\combat_drone.dm"
|
||||
|
||||
Reference in New Issue
Block a user