From 29d678deb742c5415773745ea5345b11a49fe82c Mon Sep 17 00:00:00 2001 From: Bokkiewokkie <43698041+Bokkiewokkie@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:45:32 +0100 Subject: [PATCH] Various KNPC fixes and tweaks (#2714) --- code/__HELPERS/path.dm | 23 ++-- code/game/points_of_interest.dm | 2 +- .../mob/living/carbon/human/examine.dm | 4 +- .../code/game/objects/structures/barricade.dm | 30 +++-- nsv13/code/modules/overmap/knpc.dm | 106 ++++++++++++------ nsv13/code/modules/overmap/shieldgen.dm | 2 + nsv13/code/modules/squads/squad_items.dm | 3 +- 7 files changed, 112 insertions(+), 58 deletions(-) diff --git a/code/__HELPERS/path.dm b/code/__HELPERS/path.dm index 22de777928c..1000febefbe 100644 --- a/code/__HELPERS/path.dm +++ b/code/__HELPERS/path.dm @@ -143,7 +143,7 @@ if(!start || !end) stack_trace("Invalid A* start or destination") return - if(start.z != end.z || start == end ) //no pathfinding between z levels + if(start == end) //no pathfinding between z levels //NSV13 we DO want pathfinding between z levels. removed "start.z != end.z || " from the check return if(max_distance && (max_distance < get_dist(start, end))) //if start turf is farther than max_distance from end turf, no need to do anything return @@ -337,16 +337,21 @@ * * ID: An ID card that decides if we can gain access to doors that would otherwise block a turf * * simulated_only: Do we only worry about turfs with simulated atmos, most notably things that aren't space? */ +//NSV13 refactored this to use a typecache for directional objects in src /turf/proc/LinkBlockedWithAccess(turf/destination_turf, caller, ID) var/actual_dir = get_dir(src, destination_turf) - - for(var/obj/structure/window/iter_window in src) - if(!iter_window.CanAStarPass(ID, actual_dir)) - return TRUE - - for(var/obj/machinery/door/window/iter_windoor in src) - if(!iter_windoor.CanAStarPass(ID, actual_dir)) - return TRUE + var/static/list/directionals = typecacheof(list( + /obj/structure/window, + /obj/machinery/door/window, + /obj/structure/railing, + /obj/structure/barricade, + /obj/machinery/door/firedoor + )) + + for(var/obj/iter_object in src) + if(directionals[iter_object.type]) + if(!iter_object.CanAStarPass(ID, actual_dir, caller)) + return TRUE var/reverse_dir = get_dir(destination_turf, src) for(var/obj/iter_object in destination_turf) diff --git a/code/game/points_of_interest.dm b/code/game/points_of_interest.dm index 5f901631191..18f8b858901 100644 --- a/code/game/points_of_interest.dm +++ b/code/game/points_of_interest.dm @@ -5,7 +5,7 @@ var/list/pois = list() for(var/mob/M in mobs) if(skip_mindless && (!M.mind && !M.ckey)) - if(!isbot(M) && !iscameramob(M) && !ismegafauna(M)) + if(!isbot(M) && !iscameramob(M) && !ismegafauna(M) && !isknpc(M)) //NSV13 add KNPCs continue if(M.client && M.client.holder && M.client.holder.fakekey) //stealthmins continue diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 061b5bf68d5..b6381e31b42 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -310,9 +310,9 @@ if(InCritical()) msg += "[t_He] [t_is] barely conscious.\n" if(getorgan(/obj/item/organ/brain)) - if(ai_controller?.ai_status == AI_STATUS_ON) + if(ai_controller?.ai_status == AI_STATUS_ON || isknpc(src)) //NSV13 added KNPCs msg += "[t_He] do[t_es]n't appear to be [t_him]self.\n" - if(!key) + else if(!key) msg += "[t_He] [t_is] totally catatonic. The stresses of life in deep-space must have been too much for [t_him]. Any recovery is unlikely.\n" else if(!client) msg += "[t_He] [t_has] a blank, absent-minded stare and appears completely unresponsive to anything. [t_He] may snap out of it soon.\n" diff --git a/nsv13/code/game/objects/structures/barricade.dm b/nsv13/code/game/objects/structures/barricade.dm index 3dc6abdf6ba..5b5ebe14d23 100644 --- a/nsv13/code/game/objects/structures/barricade.dm +++ b/nsv13/code/game/objects/structures/barricade.dm @@ -1,9 +1,9 @@ /obj/structure/peacekeeper_barricade //CREDIT TO CM FOR THIS. Cleanup up by Kmc. icon = 'nsv13/icons/obj/barricades.dmi' - climbable = FALSE //Disable climbing. anchored = TRUE density = TRUE layer = BELOW_OBJ_LAYER + climbable = TRUE climb_time = 20 //Leaping a barricade is universally much faster than clumsily climbing on a table or rack climb_stun = 0 var/stack_type //The type of stack the barricade dropped when disassembled if any. @@ -13,7 +13,6 @@ var/base_acid_damage = 2 var/barricade_resistance = 5 //How much force an item needs to even damage it at all. var/barricade_hitsound - var/barricade_type = "barricade" //"metal", "plasteel", etc. var/can_change_dmg_state = TRUE var/damage_state = 0 @@ -49,7 +48,6 @@ barricade_type = "plasteel" density = FALSE closed = TRUE - can_wire = TRUE /obj/structure/peacekeeper_barricade/metal/plasteel/deployable //Preset one that starts unanchored and placed down. icon_state = "plasteel_0" @@ -58,16 +56,17 @@ anchored = FALSE build_state = 0 -/* You can reenable this when you fix it, KMC. /obj/structure/peacekeeper_barricade/do_climb(var/mob/living/user) - if(is_wired) //Ohhh boy this is gonna hurt... + if(is_wired) //Even trying this is gonna hurt... user.apply_damage(10) - user.Stun(20) //Leaping into barbed wire is VERY bad - if(get_turf(user) == get_turf(src)) - usr.forceMove(get_step(src, src.dir)) + user.Stun(20) //Scaling barbed wire is VERY bad + if(loc == user.loc) + density = FALSE + . = step(user,get_step(src,src.dir)) + density = TRUE else - usr.forceMove(get_turf(src)) -*/ + . = ..() + /obj/structure/peacekeeper_barricade/metal/plasteel/attack_hand(mob/user as mob) . = ..() if(.) @@ -218,7 +217,6 @@ update_health() can_wire = FALSE is_wired = TRUE - // climbable = FALSE return FALSE if(I.tool_behaviour == TOOL_WIRECUTTER) @@ -451,6 +449,16 @@ setDir(turn(dir, 270)) return +/obj/structure/peacekeeper_barricade/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller) + . = ..() + if(.) + return + if(!(dir in to_dir)) + return TRUE + if(isknpc(caller)) //They can climb + return TRUE + return FALSE + /obj/item/stack/barbed_wire name = "barbed wire" desc = "A spiky length of wire." diff --git a/nsv13/code/modules/overmap/knpc.dm b/nsv13/code/modules/overmap/knpc.dm index 8c655df0ca5..82917758956 100644 --- a/nsv13/code/modules/overmap/knpc.dm +++ b/nsv13/code/modules/overmap/knpc.dm @@ -20,14 +20,19 @@ GLOBAL_LIST_EMPTY(knpcs) var/obj/effect/landmark/patrol_node/last_node = null //What was the last patrol node we visited? var/stealing_id = FALSE var/next_internals_attempt = 0 - var/static/list/climbable = typecacheof(list(/obj/structure/table, /obj/structure/railing)) // climbable structures + var/static/list/climbable = typecacheof(list( + /obj/structure/table, + /obj/structure/railing, + /obj/structure/peacekeeper_barricade, + /obj/item/ship_weapon/ammunition + )) // climbable things var/pathfind_timeout = 0 //If pathfinding fails, it is püt in timeout for a while to avoid spamming the server with pathfinding calls. var/timeout_stacks = 0 //Consecutive pathfind fails add additional delay stacks to further counteract the effects of knpcs in unreachable locations. /mob/living/carbon/human/ai_boarder faction = list("Neutral") - var/move_delay = 4 //How quickly do the boys travel? - var/action_delay = 6 //How long we delay between actions + var/move_delay = 4 //How quickly do the boys travel? + var/action_delay = 9 //How long we delay between actions var/knpc_traits = KNPC_IS_DODGER | KNPC_IS_MERCIFUL | KNPC_IS_AREA_SPECIFIC var/difficulty_override = FALSE //Whether to ignore overmap difficulty or not var/list/outfit = list ( @@ -143,6 +148,8 @@ GLOBAL_LIST_EMPTY(knpcs) if(length(path) > 1) var/turf/next_turf = get_step_towards(H, path[1]) var/turf/this_turf = get_turf(H) + var/move_dir = get_dir(this_turf, next_turf) + var/reverse_dir = get_dir(next_turf, this_turf) //Walk when you see a wet floor if(next_turf.GetComponent(/datum/component/wet_floor)) H.m_intent = MOVE_INTENT_WALK @@ -150,32 +157,53 @@ GLOBAL_LIST_EMPTY(knpcs) H.m_intent = MOVE_INTENT_RUN for(var/obj/machinery/door/firedoor/blocking_firelock in next_turf) - if((blocking_firelock.flags_1 & ON_BORDER_1) && !(blocking_firelock.dir in dir_to_cardinal_dirs(get_dir(next_turf, this_turf)))) + if((blocking_firelock.flags_1 & ON_BORDER_1) && !(blocking_firelock.dir in dir_to_cardinal_dirs(reverse_dir))) //Here, only firelocks on the border matter since fulltile firelocks let you exit. continue - if(!blocking_firelock.density || blocking_firelock.operating) + if(!blocking_firelock.density || blocking_firelock.powered()) continue - if(blocking_firelock.welded) + if((blocking_firelock.welded)) break //If at least one firedoor in our way is welded shut, welp! blocking_firelock.open() //Open one firelock per tile per try. break for(var/obj/machinery/door/firedoor/blocking_firelock in this_turf) - if(!((blocking_firelock.flags_1 & ON_BORDER_1) && (blocking_firelock.dir in dir_to_cardinal_dirs(get_dir(this_turf, next_turf))))) //Here, only firelocks on the border matter since fulltile firelocks let you exit. + if(!((blocking_firelock.flags_1 & ON_BORDER_1) && (blocking_firelock.dir in dir_to_cardinal_dirs(move_dir)))) continue - if(!blocking_firelock.density || blocking_firelock.operating) + if(!blocking_firelock.density || blocking_firelock.powered()) continue if(blocking_firelock.welded) break //If at least one firedoor in our way is welded shut, welp! blocking_firelock.open() //Open one firelock per tile per try. break for(var/obj/structure/possible_barrier in next_turf) //If we're stuck - if(!climbable.Find(possible_barrier.type)) + if(!climbable[possible_barrier.type]) + continue + if(possible_barrier.dir == reverse_dir || istype(possible_barrier, /obj/structure/table)) + var/obj/item/dropped_it + if(H.get_active_held_item()) + dropped_it = H.get_active_held_item() + possible_barrier.climb_structure(H) + if(dropped_it) //Don't forget to pick up your stuff + H.put_in_hands(dropped_it, forced=TRUE) + else continue - H.forceMove(next_turf) - H.visible_message("[H] climbs onto [possible_barrier]!") - H.Stun(2 SECONDS) //Table. if(get_turf(H) == path[1]) increment_path() - return TRUE + return TRUE + for(var/obj/structure/possible_barrier in this_turf) + if(!climbable[possible_barrier.type]) + continue + if(possible_barrier.dir == move_dir || istype(possible_barrier, /obj/structure/table)) + var/obj/item/dropped_it + if(H.get_active_held_item()) + dropped_it = H.get_active_held_item() + possible_barrier.climb_structure(H) + if(dropped_it) //Don't forget to pick up your stuff + H.put_in_hands(dropped_it, forced=TRUE) + else + continue + if(get_turf(H) == path[1]) + increment_path() + return TRUE step_towards(H, path[1]) if(get_turf(H) == path[1]) //Successful move increment_path() @@ -255,6 +283,7 @@ GLOBAL_LIST_EMPTY(knpcs) if(length(path)) next_path_step() else //They should always be pathing somewhere... + next_path_step() dest = null tries = 0 path = list() @@ -298,7 +327,7 @@ of a specific action goes up, to encourage skynet to go for that one instead. var/list/guessed_objects = view(HA.guess_range, HA.parent) for(var/mob/living/M in guessed_objects) //Invis is a no go. Non-human, -cyborg or -hostile mobs are ignored. - if(M.invisibility >= INVISIBILITY_ABSTRACT || M.alpha <= 0 || (!ishuman(M) && !iscyborg(M) && !ishostile(M))) + if(M.invisibility >= INVISIBILITY_ABSTRACT || M.alpha <= 0 || (!ishuman(M) && !iscyborg(M))) //Removed && !ishostile(M) temporarily because of Sgt. Araneus continue // Dead mobs are ignored. if(CHECK_BITFIELD(H.knpc_traits, KNPC_IS_MERCIFUL) && M.stat >= UNCONSCIOUS) @@ -353,13 +382,14 @@ This is to account for sec Ju-Jitsuing boarding commandos. if(!..()) return 0 var/mob/living/carbon/human/H = HA.parent - var/obj/item/gun/G = H.get_active_held_item() + var/obj/A = H.get_active_held_item() + var/obj/B = H.get_inactive_held_item() //check your other hand, just in case. //We already have a gun - if(G && istype(G)) + if((A && istype(A, /obj/item/gun)) || (B && istype(B, /obj/item/gun))) return 0 - var/obj/item/gun/G_New = locate(/obj/item/gun) in oview(HA.view_range, H) - if(G_New && gun_suitable(H, G_New)) - return AI_SCORE_CRITICAL //There is a gun really obviously in the open.... + for(var/obj/item/gun/G in view(HA.view_range, H)) + if(gun_suitable(H, G)) + return AI_SCORE_CRITICAL //There is a gun really obviously in the open.... return score /datum/ai_goal/human/proc/CheckFriendlyFire(mob/living/us, mob/living/them) @@ -376,7 +406,16 @@ This is to account for sec Ju-Jitsuing boarding commandos. var/mob/living/carbon/human/H = HA.parent var/obj/item/storage/S = H.back var/obj/item/gun/target_item = null - //Okay first off, is the gun already on our person? + //We must have lost our gun somehow, get it from the floor if we simply dropped it. + if(istype(H.loc, /turf)) + var/turf/T = H.loc + for(var/obj/item/gun/G in T.contents) + if(gun_suitable(H, G)) + target_item = G + break + if(target_item && H.put_in_hands(target_item)) + return TRUE + //Otherwise, is there a gun already on our person? if(S) var/list/expanded_contents = S.contents + H.contents target_item = locate(/obj/item/gun) in expanded_contents @@ -386,7 +425,7 @@ This is to account for sec Ju-Jitsuing boarding commandos. target_item.forceMove(get_turf(H)) //Put it on the floor so they can grab it if(H.put_in_hands(target_item)) return TRUE //We're done! - //Now we run the more expensive check to find a gun laying on the ground. + //Now we run the more expensive check to find a gun farther away. var/best_distance = world.maxx for(var/obj/O in oview(HA.view_range, H)) var/dist = get_dist(O, H) @@ -447,34 +486,33 @@ This is to account for sec Ju-Jitsuing boarding commandos. if(E.selfcharge) //Okay good, it self charges we can just wait. return TRUE else //Discard it, we're not gonna teach them to use rechargers yet. - E.forceMove(get_turf(H)) + H.dropItemToGround(E) return FALSE if(istype(gun, /obj/item/gun/ballistic)) var/obj/item/gun/ballistic/B = gun if(istype(B.mag_type, /obj/item/ammo_box/magazine/internal)) //Not dealing with this. They'll just ditch the revolver when they're done with it. - B.forceMove(get_turf(H)) + H.dropItemToGround(B) return FALSE - ///message_admins("Issa gun") - var/obj/item/storage/S = H.back + var/obj/item/storage/backpack = H.back //Okay first off, is the gun already on our person? var/list/expanded_contents = H.contents - if(S) - expanded_contents = S.contents + H.contents + if(backpack) + expanded_contents = backpack.contents + H.contents var/obj/item/ammo_box/magazine/target_mag = locate(B.mag_type) in expanded_contents - //message_admins("Found [target_mag]") if(target_mag) //Dump that old mag H.put_in_inactive_hand(target_mag) - B?.magazine?.forceMove(get_turf(H)) - B.attackby(target_mag, H) + B.eject_magazine(H, FALSE, target_mag) //Tacticool reloads + H.dropItemToGround(H.get_inactive_held_item()) //We don't need that magazine anymore B.attack_self(H) //Rack the bolt. else - if(!S) - gun.forceMove(get_turf(H)) + if(!backpack) + H.dropItemToGround(B) return FALSE - gun.forceMove(S) - + backpack.melee_attack_chain(src, B) + if(H.is_holding(B)) //No space in the backpack, this is useless to us so drop it + H.dropItemToGround(B) /datum/ai_goal/human/engage_targets/action(datum/component/knpc/HA) if(!can_action(HA)) diff --git a/nsv13/code/modules/overmap/shieldgen.dm b/nsv13/code/modules/overmap/shieldgen.dm index 55376675734..6db73ebbb77 100644 --- a/nsv13/code/modules/overmap/shieldgen.dm +++ b/nsv13/code/modules/overmap/shieldgen.dm @@ -204,6 +204,8 @@ var/obj/structure/cable/cable = null //Connected cable var/mutable_appearance/c_screen +/obj/machinery/shield_generator/update_icon() + cut_overlays() /obj/machinery/shield_generator/proc/absorb_hit(obj/item/projectile/proj) var/damage = proj.damage diff --git a/nsv13/code/modules/squads/squad_items.dm b/nsv13/code/modules/squads/squad_items.dm index 86b58a3d305..a96e2f315e3 100644 --- a/nsv13/code/modules/squads/squad_items.dm +++ b/nsv13/code/modules/squads/squad_items.dm @@ -406,7 +406,8 @@ . = ..() if(torn) return - inflate(get_turf(target), user) + if(in_range(target, user)) + inflate(get_turf(target), user) /obj/item/inflatable/proc/inflate(turf/target, mob/user) playsound(loc, 'sound/items/zip.ogg', 75, 1)