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)