diff --git a/code/modules/ai/ai_holder_combat_unseen.dm b/code/modules/ai/ai_holder_combat_unseen.dm index c854377c89..5c29f8a31b 100644 --- a/code/modules/ai/ai_holder_combat_unseen.dm +++ b/code/modules/ai/ai_holder_combat_unseen.dm @@ -7,27 +7,30 @@ if(conserve_ammo || !holder.ICheckRangedAttack(target_last_seen_turf)) if(get_dist(holder, target_last_seen_turf) > 1) // We last saw them over there. // Go to where you last saw the enemy. - give_destination(target_last_seen_turf, 1, TRUE) // Sets stance as well - else if(lose_target_time == world.time) // We last saw them next to us, so do a blind attack on that tile. - melee_on_tile(target_last_seen_turf) + return give_destination(target_last_seen_turf, 1, TRUE) // Sets stance as well + else if(lose_target_time < world.time) // We last saw them next to us, so do a blind attack on that tile. + if(melee_on_tile(target_last_seen_turf) != ATTACK_SUCCESSFUL && intelligence_level >= AI_NORMAL) + var/obj/O = find_escape_route() + if(istype(O)) + return give_destination(get_turf(O), 0, TRUE) else - find_target() + return find_target() else - shoot_near_turf(target_last_seen_turf) + return shoot_near_turf(target_last_seen_turf) // This shoots semi-randomly near a specific turf. /datum/ai_holder/proc/shoot_near_turf(turf/targeted_turf) if(get_dist(holder, targeted_turf) > max_range(targeted_turf)) - return // Too far to shoot. + return ATTACK_FAILED// Too far to shoot. var/turf/T = pick(RANGE_TURFS(2, targeted_turf)) // The turf we're actually gonna shoot at. on_engagement(T) if(firing_lanes && !test_projectile_safety(T)) step_rand(holder) holder.face_atom(T) - return + return ATTACK_FAILED - ranged_attack(T) + return ranged_attack(T) // Attempts to attack something on a specific tile. // TODO: Put on mob/living? @@ -36,9 +39,53 @@ var/mob/living/L = locate() in T if(!L) T.visible_message("\The [holder] attacks nothing around \the [T].") - return + return ATTACK_FAILED if(holder.IIsAlly(L)) // Don't hurt our ally. - return + return ATTACK_FAILED - melee_attack(L) \ No newline at end of file + return melee_attack(L) + +// Attempts to locate any possible avenues that the target might have escaped via +// Could be an open door, could be a stairwell or a ladder +// Returns object to path to. If multiple targets are equidistant, picks randomly +/datum/ai_holder/proc/find_escape_route() + ai_log("find_escape_route() : Entering.", AI_LOG_DEBUG) + var/list/closest_escape = list() + var/closest_dist = world.view // We can't see any further than this + var/list/possible_escape_types = list( + /obj/machinery/door, + /obj/structure/stairs/top, + /obj/structure/stairs/bottom + ) + + if(intelligence_level >= AI_SMART) + possible_escape_types += /obj/structure/ladder + + for(var/atom/A in view(world.view, holder)) + if(!is_type_in_list(A, possible_escape_types)) + continue // Not something they could have escaped through + if(turn(holder.dir, 180) & get_dir(get_turf(holder), get_turf(A))) + continue // Surely, they couldn't have escaped *behind* us! + + if(istype(A, /obj/machinery/door)) + var/obj/machinery/door/D = A + if(D.glass) // Surely, they couldn't hide behind a transparent door! + continue + if(D.density && intelligence_level < AI_SMART) // Surely, they couldn't have escaped through a *closed* door + continue + + var/dist = get_dist(holder, A) + if(dist == closest_dist) + closest_escape += A + + else if(dist < closest_dist) + closest_escape.Cut() + closest_escape += A + closest_dist = dist + ai_log("find_escape_route() : Found [closest_escape.len] candidates [closest_dist] tiles away.", AI_LOG_DEBUG) + if(closest_escape.len) + return pick(closest_escape) + return null + + \ No newline at end of file diff --git a/code/modules/ai/ai_holder_movement.dm b/code/modules/ai/ai_holder_movement.dm index 3f9eb1b685..bc5916917a 100644 --- a/code/modules/ai/ai_holder_movement.dm +++ b/code/modules/ai/ai_holder_movement.dm @@ -27,19 +27,19 @@ ai_log("walk_to_destination() : Exiting.", AI_LOG_TRACE) return - var/get_to = min_distance_to_destination var/distance = get_dist(holder, destination) - ai_log("walk_to_destination() : get_to is [get_to].", AI_LOG_TRACE) + ai_log("walk_to_destination() : get_to is [min_distance_to_destination].", AI_LOG_TRACE) - // We're here! - if(distance <= get_to) + // We're here! Or we're horribly lost + if(distance <= min_distance_to_destination || holder.z != destination.z) + check_use_ladder() give_up_movement() set_stance(stance == STANCE_REPOSITION ? STANCE_APPROACH : STANCE_IDLE) ai_log("walk_to_destination() : Destination reached. Exiting.", AI_LOG_INFO) return ai_log("walk_to_destination() : Walking.", AI_LOG_TRACE) - walk_path(destination, get_to) + walk_path(destination, min_distance_to_destination) ai_log("walk_to_destination() : Exiting.",AI_LOG_TRACE) /datum/ai_holder/proc/should_go_home() @@ -159,3 +159,34 @@ holder.IMove(get_step(holder,moving_to)) wander_delay = base_wander_delay ai_log("handle_wander_movement() : Exited.", AI_LOG_TRACE) + +/datum/ai_holder/proc/check_use_ladder() + // No target, don't use the ladder + // Target is visible, don't use the ladder + if(!target || can_see_target(target)) + return + + var/has_hands = TRUE + if(istype(holder, /mob/living/simple_mob)) + var/mob/living/simple_mob/S = holder + has_hands = S.has_hands + + // Don't have means to use a ladder or the space around it, don't use the ladder + if(!has_hands && !holder.hovering) + return + + var/obj/structure/ladder/L = locate() in get_turf(holder) + if(!istype(L)) + return // No ladder, can't use it + + if(!holder.may_climb_ladders(L)) + return // Can't climb the ladder for other reasons (Probably inconsequential?) + + var/list/directions = list() + if(L.allowed_directions & DOWN) + directions += L.target_down + if(L.allowed_directions & UP) + directions += L.target_up + + if(directions.len) + L.climbLadder(holder, pick(directions)) \ No newline at end of file diff --git a/code/modules/ai/ai_holder_targeting.dm b/code/modules/ai/ai_holder_targeting.dm index 240d782926..3647c53cc5 100644 --- a/code/modules/ai/ai_holder_targeting.dm +++ b/code/modules/ai/ai_holder_targeting.dm @@ -165,19 +165,14 @@ target = null lose_target_time = world.time - give_up_movement() - - if(target_last_seen_turf && intelligence_level >= AI_SMART) + if(target_last_seen_turf && intelligence_level >= AI_NORMAL) ai_log("lose_target() : Going into 'engage unseen enemy' mode.", AI_LOG_INFO) - engage_unseen_enemy() - return TRUE //We're still working on it + return engage_unseen_enemy() //We're still working on it else ai_log("lose_target() : Can't chase target, so giving up.", AI_LOG_INFO) remove_target() return find_target() //Returns if we found anything else to do - return FALSE //Nothing new to do - // 'Hard' loss of target. Clean things up and return to idle. /datum/ai_holder/proc/remove_target() ai_log("remove_target() : Entering.", AI_LOG_TRACE)