More spiders.

This commit is contained in:
Neerti
2018-05-05 13:26:22 -04:00
parent 5772ad84ac
commit 98ea2b1a2c
31 changed files with 1007 additions and 109 deletions
@@ -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.
*/
+25 -10
View File
@@ -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)
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -95,7 +95,7 @@
use_astar = TRUE
/datum/ai_holder/hostile/ranged
ranged = TRUE
// ranged = TRUE
cooperative = TRUE
firing_lanes = TRUE
conserve_ammo = TRUE
+8 -1
View File
@@ -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
+25 -7
View File
@@ -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
+19 -1
View File
@@ -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)