From 255c0970ca12d09cb4dfa40c86807a1b316789c2 Mon Sep 17 00:00:00 2001 From: Aronai Sieyes Date: Tue, 17 Mar 2020 21:55:23 -0400 Subject: [PATCH 1/2] AI, give up breaking things if there's nothing to break --- code/modules/ai/_defines.dm | 5 +++++ code/modules/ai/ai_holder_combat.dm | 20 ++++++++++---------- code/modules/ai/ai_holder_movement.dm | 11 ++++++++--- code/modules/ai/interfaces.dm | 4 ++-- code/modules/mob/living/simple_mob/combat.dm | 8 +++----- 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/code/modules/ai/_defines.dm b/code/modules/ai/_defines.dm index e94d26b3c3..7b49f18ddb 100644 --- a/code/modules/ai/_defines.dm +++ b/code/modules/ai/_defines.dm @@ -20,6 +20,11 @@ #define MOVEMENT_FAILED 0 // Move() returned false for whatever reason and the mob didn't move. #define MOVEMENT_SUCCESSFUL 1 // Move() returned true and the mob hopefully moved. +// Results of pre-attack checks +#define ATTACK_ON_COOLDOWN -1 // Recently attacked and needs to try again soon. +#define ATTACK_FAILED 0 // Something else went wrong! Maybe they moved away! +#define ATTACK_SUCCESSFUL 1 // We attacked (or tried to, misses count too) + // Reasons for targets to not be valid. Based on why, the AI responds differently. #define AI_TARGET_VALID 0 // We can fight them. #define AI_TARGET_INVIS 1 // They were in field of view but became invisible. Switch to STANCE_BLINDFIGHT if no other viable targets exist. diff --git a/code/modules/ai/ai_holder_combat.dm b/code/modules/ai/ai_holder_combat.dm index 65fd1785b1..d961fcc171 100644 --- a/code/modules/ai/ai_holder_combat.dm +++ b/code/modules/ai/ai_holder_combat.dm @@ -70,21 +70,22 @@ /datum/ai_holder/proc/melee_attack(atom/A) pre_melee_attack(A) . = holder.IAttack(A) - if(.) + world.log << "melee_attack: [.]" + if(. == ATTACK_SUCCESSFUL) post_melee_attack(A) // Ditto. /datum/ai_holder/proc/ranged_attack(atom/A) pre_ranged_attack(A) . = holder.IRangedAttack(A) - if(.) + if(. == ATTACK_SUCCESSFUL) post_ranged_attack(A) // Most mobs probably won't have this defined but we don't care. /datum/ai_holder/proc/special_attack(atom/movable/AM) pre_special_attack(AM) . = holder.ISpecialAttack(AM) - if(.) + if(. == ATTACK_SUCCESSFUL) post_special_attack(AM) // Called when within striking/shooting distance, however cooldown is not considered. @@ -218,7 +219,6 @@ var/dir_to_target = get_dir(holder, target_atom) holder.face_atom(target_atom) - ai_log("breakthrough() : Exiting", AI_LOG_DEBUG) // Sometimes the mob will try to hit something diagonally, and generally this fails. // So instead we will try two more times with some adjustments if the attack fails. @@ -274,31 +274,31 @@ for(var/obj/structure/window/W in problem_turf) if(W.dir == reverse_dir[holder.dir]) // So that windows get smashed in the right order ai_log("destroy_surroundings() : Attacking side window.", AI_LOG_INFO) - return holder.IAttack(W) + return melee_attack(W) else if(W.is_fulltile()) ai_log("destroy_surroundings() : Attacking full tile window.", AI_LOG_INFO) - return holder.IAttack(W) + return melee_attack(W) // Kill hull shields in the way. for(var/obj/effect/energy_field/shield in problem_turf) if(shield.density) // Don't attack shields that are already down. ai_log("destroy_surroundings() : Attacking hull shield.", AI_LOG_INFO) - return holder.IAttack(shield) + return melee_attack(shield) // Kill common obstacle in the way like tables. var/obj/structure/obstacle = locate(/obj/structure, problem_turf) if(istype(obstacle, /obj/structure/window) || istype(obstacle, /obj/structure/closet) || istype(obstacle, /obj/structure/table) || istype(obstacle, /obj/structure/grille)) ai_log("destroy_surroundings() : Attacking generic structure.", AI_LOG_INFO) - return holder.IAttack(obstacle) + return melee_attack(obstacle) for(var/obj/machinery/door/D in problem_turf) // Required since firelocks take up the same turf. if(D.density) ai_log("destroy_surroundings() : Attacking closed door.", AI_LOG_INFO) - return holder.IAttack(D) + return melee_attack(D) ai_log("destroy_surroundings() : Exiting due to nothing to attack.", AI_LOG_INFO) - return FALSE // Nothing to attack. + return ATTACK_FAILED // Nothing to attack. // Override for special behaviour. /datum/ai_holder/proc/can_violently_breakthrough() diff --git a/code/modules/ai/ai_holder_movement.dm b/code/modules/ai/ai_holder_movement.dm index 58b8c9d5ee..eb465dec5d 100644 --- a/code/modules/ai/ai_holder_movement.dm +++ b/code/modules/ai/ai_holder_movement.dm @@ -9,13 +9,14 @@ var/home_low_priority = FALSE // If true, the mob will not go home unless it has nothing better to do, e.g. its following someone. var/max_home_distance = 3 // How far the mob can go away from its home before being told to go_home(). // Note that there is a 'BYOND cap' of 14 due to limitations of get_/step_to(). - // Wandering. var/wander = FALSE // If true, the mob will randomly move in the four cardinal directions when idle. var/wander_delay = 0 // How many ticks until the mob can move a tile in handle_wander_movement(). var/base_wander_delay = 2 // What the above var gets set to when it wanders. Note that a tick happens every half a second. var/wander_when_pulled = FALSE // If the mob will refrain from wandering if someone is pulling it. + // Breakthrough + var/failed_breakthroughs = 0 // How many times we've failed to breakthrough something lately /datum/ai_holder/proc/walk_to_destination() ai_log("walk_to_destination() : Entering.",AI_LOG_TRACE) @@ -90,7 +91,9 @@ // step_to(holder, A) if(holder.IMove(get_step_to(holder, A)) == MOVEMENT_FAILED) ai_log("walk_path() : Failed to move, attempting breakthrough.", AI_LOG_INFO) - breakthrough(A) // We failed to move, time to smash things. + if(!breakthrough(A) && failed_breakthroughs++ >= 5) // We failed to move, time to smash things. + give_up_movement() + failed_breakthroughs = 0 return if(move_once() == FALSE) // Start walking the path. @@ -106,7 +109,9 @@ ai_log("walk_path() : Going to IMove().", AI_LOG_TRACE) if(holder.IMove(get_step_to(holder, A)) == MOVEMENT_FAILED ) ai_log("walk_path() : Failed to move, attempting breakthrough.", AI_LOG_INFO) - breakthrough(A) // We failed to move, time to smash things. + if(!breakthrough(A) && failed_breakthroughs++ >= 5) // We failed to move, time to smash things. + give_up_movement() + failed_breakthroughs = 0 ai_log("walk_path() : Exited.", AI_LOG_TRACE) diff --git a/code/modules/ai/interfaces.dm b/code/modules/ai/interfaces.dm index 59ffbeea72..b4323782d3 100644 --- a/code/modules/ai/interfaces.dm +++ b/code/modules/ai/interfaces.dm @@ -8,7 +8,7 @@ /mob/living/simple_mob/IAttack(atom/A) if(!canClick()) // Still on cooldown from a "click". - return FALSE + return ATTACK_ON_COOLDOWN return attack_target(A) // This will set click cooldown. /mob/living/proc/IRangedAttack(atom/A) @@ -16,7 +16,7 @@ /mob/living/simple_mob/IRangedAttack(atom/A) if(!canClick()) // Still on cooldown from a "click". - return FALSE + return ATTACK_ON_COOLDOWN return shoot_target(A) // Test if the AI is allowed to attempt a ranged attack. diff --git a/code/modules/mob/living/simple_mob/combat.dm b/code/modules/mob/living/simple_mob/combat.dm index 0f3205fb8c..046a4ef988 100644 --- a/code/modules/mob/living/simple_mob/combat.dm +++ b/code/modules/mob/living/simple_mob/combat.dm @@ -3,26 +3,24 @@ set waitfor = FALSE // For attack animations. Don't want the AI processor to get held up. if(!A.Adjacent(src)) - return FALSE + return ATTACK_FAILED var/turf/their_T = get_turf(A) face_atom(A) if(melee_attack_delay) - // their_T.color = "#FF0000" melee_pre_animation(A) + . = ATTACK_SUCCESSFUL //Shoving this in here as a 'best guess' since this proc is about to sleep and return and we won't be able to know the real value handle_attack_delay(A, melee_attack_delay) // 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()) + // Returns a value, but will be lost if . = do_attack(A, their_T) if(melee_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. From a3ef4b7f714946580ffbce78ebb3e619afb9f4d5 Mon Sep 17 00:00:00 2001 From: Aronai Sieyes Date: Wed, 18 Mar 2020 09:54:55 -0400 Subject: [PATCH 2/2] Remove unintentional debug statement --- code/modules/ai/ai_holder_combat.dm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/modules/ai/ai_holder_combat.dm b/code/modules/ai/ai_holder_combat.dm index d961fcc171..64dfa7d17e 100644 --- a/code/modules/ai/ai_holder_combat.dm +++ b/code/modules/ai/ai_holder_combat.dm @@ -70,7 +70,6 @@ /datum/ai_holder/proc/melee_attack(atom/A) pre_melee_attack(A) . = holder.IAttack(A) - world.log << "melee_attack: [.]" if(. == ATTACK_SUCCESSFUL) post_melee_attack(A) @@ -302,4 +301,4 @@ // Override for special behaviour. /datum/ai_holder/proc/can_violently_breakthrough() - return violent_breakthrough \ No newline at end of file + return violent_breakthrough