Files
Bubberstation/code/datums/proximity_monitor/field.dm
Swift 67a3eb415d [UPSTREAM] [NO GBP] Slime AI fixes + prerequisites (#1432)
…not idle) (#27470)

* Reworks targeting behavior to fall back onto proximity monitors.
Refactors ai cooldowns a bit (#82640)

Nother bit ripped out of #79498
[Implements a get_cooldown() proc to get around dumb manual overrides
and empower me to optimize the findtarget

logic](7047d294dd)

[Adds modify_cooldown, uses it to optimize find_potential_targets
further](4ebc8cedce)

No sense running the behavior if we're just waiting on its output, so
let's run it once a minute just in case, then push an update instantly
if we find something

[Optimizes connect_range and

promxity_monitors](bcf7d7c5b3)

We know what turfs exist before and after a move
We can use this information to prevent trying to update turfs we don't
care about.

This is important because post these changes mobs with fields will be
moving a lot more, so it's gotta be cheap

[Implements a special kind of field to handle ai

targeting](80b63b3445)

If we run targeting and don't like, find anything, we should setup a
field that listens for things coming near us and then handle those
things as we find them.

This incurs a slight startup cost but saves so much time on the churn of
constant costs

Note:
We should also work to figure out a way to avoid waking ais if none is
near them/they aren't doing anything interesting

We don't need to do that immediately this acts as somewhat of a stopgap
(and would be good regardless) but it is worth keeping in mind)

I am unsure whether this is worth it anymore since #82539 was merged. As
I say it was done as a stopgap because ais didn't know how to idle. If
not I'll rip er out and we'll keep the other
refactoring/optimizations.

Cleaner basic ai code, maybe? faster basic ai code, for sure faster
proximity monitors (significantly)

* ai controllers use cell trackers to know when to idle (#82691)

this makes ai controllers use cell trackers and signals to determine
when to idle

might be better than looping over all clients for every controller

🆑
code: The way mobs idle has been refactored, please report any issues
with non-reactive mobs
/🆑

* makes slimes not idle (#82742)

slimes should still be able to do their everyday routine without needing
to be watched over

makes xenobiologist's lives easier

🆑
qol: slimes will stay active without needing any one to watch over /🆑

---------

<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may
not be viewable. -->
<!-- You can view Contributing.MD for a detailed description of the pull
request process. -->

## About The Pull Request

<!-- Describe The Pull Request. Please be sure every change is
documented or this can delay review and even discourage maintainers from
merging your PR! -->

<!-- Please make sure to actually test your PRs. If you have not tested
your PR mention it. -->

## Why It's Good For The Game

<!-- Argue for the merits of your changes and how they benefit the game,
especially if they are controversial and/or far reaching. If you can't
actually explain WHY what you are doing will improve the game, then it
probably isn't good for the game in the first place. -->

## Proof Of Testing

<!-- Compile and run your code locally. Make sure it works. This is the
place to show off your changes! We are not responsible for testing your
features. -->

## Changelog

<!-- If your PR modifies aspects of the game that can be concretely
observed by players or admins you should add a changelog. If your change
does NOT meet this description, remove this section. Be sure to properly
mark your PRs to prevent unnecessary GBP loss. You can read up on GBP
and it's effects on PRs in the tgstation guides for contributors. Please
note that maintainers freely reserve the right to remove and add tags
should they deem it appropriate. You can attempt to finagle the system
all you want, but it's best to shoot for clear communication right off
the bat. -->

🆑
add: Added new mechanics or gameplay changes
add: Added more things
del: Removed old things
qol: made something easier to use
balance: rebalanced something
fix: fixed a few things
sound: added/modified/removed audio or sound effects
image: added/modified/removed some icons or images
spellcheck: fixed a few typos
code: changed some code
refactor: refactored some code
config: changed some config setting
admin: messed with admin stuff
server: something server ops should know
/🆑

<!-- Both 🆑's are required for the changelog to work! You can put
your name to the right of the first 🆑 if you want to overwrite your
GitHub username as author ingame. -->
<!-- You can use multiple of the same prefix (they're only used for the
icon ingame) and delete the unneeded ones. Despite some of the tags,
changelogs should generally represent how a player might be affected by
the changes rather than a summary of the PR's contents. -->

<!-- By opening a pull request. You have read and understood the
repository rules located on the main README.md on this project. -->

---------

Co-authored-by: Waterpig <49160555+Majkl-J@users.noreply.github.com>
Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com>
Co-authored-by: SkyratBot <59378654+SkyratBot@users.noreply.github.com>
Co-authored-by: Iamgoofball <iamgoofball@gmail.com>
Co-authored-by: Waterpig <wtryoutube@seznam.cz>
2024-04-25 23:13:16 -05:00

220 lines
8.0 KiB
Plaintext

#define FIELD_TURFS_KEY "field_turfs"
#define EDGE_TURFS_KEY "edge_turfs"
/**
* Movable and easily code-modified fields! Allows for custom AOE effects that affect movement
* and anything inside of them, and can do custom turf effects!
* Supports automatic recalculation/reset on movement.
*
* "What do I gain from using advanced over standard prox monitors?"
* - You can set different effects on edge vs field entrance
* - You can set effects when the proximity monitor starts and stops tracking a turf
*/
/datum/proximity_monitor/advanced
/// If TRUE, edge turfs will be included as "in the field" for effects
/// Can be used in certain situations where you may have effects that trigger only at the edge,
/// while also wanting the field effect to trigger at edge turfs as well
var/edge_is_a_field = FALSE
/// All turfs on the inside of the proximity monitor - range - 1 turfs
var/list/turf/field_turfs = list()
/// All turfs on the very last tile of the proximity monitor's radius
var/list/turf/edge_turfs = list()
/datum/proximity_monitor/advanced/Destroy()
cleanup_field()
return ..()
/datum/proximity_monitor/advanced/proc/cleanup_field()
for(var/turf/turf as anything in edge_turfs)
cleanup_edge_turf(turf)
edge_turfs = list()
for(var/turf/turf as anything in field_turfs)
cleanup_field_turf(turf)
field_turfs = list()
//Call every time the field moves (done automatically if you use update_center) or a setup specification is changed.
/datum/proximity_monitor/advanced/proc/recalculate_field(full_recalc = FALSE)
var/list/new_turfs = update_new_turfs()
var/list/old_field_turfs = field_turfs
var/list/old_edge_turfs = edge_turfs
field_turfs = new_turfs[FIELD_TURFS_KEY]
edge_turfs = new_turfs[EDGE_TURFS_KEY]
if(!full_recalc)
field_turfs = list()
edge_turfs = list()
for(var/turf/old_turf as anything in old_field_turfs - field_turfs)
if(QDELETED(src))
return
cleanup_field_turf(old_turf)
for(var/turf/old_turf as anything in old_edge_turfs - edge_turfs)
if(QDELETED(src))
return
cleanup_edge_turf(old_turf)
if(full_recalc)
old_field_turfs = list()
old_edge_turfs = list()
field_turfs = new_turfs[FIELD_TURFS_KEY]
edge_turfs = new_turfs[EDGE_TURFS_KEY]
for(var/turf/new_turf as anything in field_turfs - old_field_turfs)
if(QDELETED(src))
return
field_turfs += new_turf
setup_field_turf(new_turf)
for(var/turf/new_turf as anything in edge_turfs - old_edge_turfs)
if(QDELETED(src))
return
edge_turfs += new_turf
setup_edge_turf(new_turf)
/datum/proximity_monitor/advanced/on_initialized(turf/location, atom/created, init_flags)
. = ..()
on_entered(location, created, null)
/datum/proximity_monitor/advanced/on_entered(turf/source, atom/movable/entered, turf/old_loc)
. = ..()
if(get_dist(source, host) == current_range)
field_edge_crossed(entered, old_loc, source)
else
field_turf_crossed(entered, old_loc, source)
/datum/proximity_monitor/advanced/on_moved(atom/movable/movable, atom/old_loc)
. = ..()
if(ignore_if_not_on_turf)
//Early return if it's not the host that has moved.
if(movable != host)
return
//Cleanup the field if the host was on a turf but isn't anymore.
if(!isturf(host.loc))
if(isturf(old_loc))
cleanup_field()
return
recalculate_field(full_recalc = FALSE)
/datum/proximity_monitor/advanced/on_uncrossed(turf/source, atom/movable/gone, direction)
if(get_dist(source, host) == current_range)
field_edge_uncrossed(gone, source, get_turf(gone))
else
field_turf_uncrossed(gone, source, get_turf(gone))
/// Called when a turf in the field of the monitor is linked
/datum/proximity_monitor/advanced/proc/setup_field_turf(turf/target)
return
/// Called when a turf in the field of the monitor is unlinked
/// Do NOT call this manually, requires management of the field_turfs list
/datum/proximity_monitor/advanced/proc/cleanup_field_turf(turf/target)
return
/// Called when a turf in the edge of the monitor is linked
/datum/proximity_monitor/advanced/proc/setup_edge_turf(turf/target)
if(edge_is_a_field) // If the edge is considered a field, set it up like one
setup_field_turf(target)
/// Called when a turf in the edge of the monitor is unlinked
/// Do NOT call this manually, requires management of the edge_turfs list
/datum/proximity_monitor/advanced/proc/cleanup_edge_turf(turf/target)
if(edge_is_a_field) // If the edge is considered a field, clean it up like one
cleanup_field_turf(target)
/datum/proximity_monitor/advanced/proc/update_new_turfs()
if(ignore_if_not_on_turf && !isturf(host.loc))
return list(FIELD_TURFS_KEY = list(), EDGE_TURFS_KEY = list())
var/list/local_field_turfs = list()
var/list/local_edge_turfs = list()
var/turf/center = get_turf(host)
if(current_range > 0)
local_field_turfs += RANGE_TURFS(current_range - 1, center)
if(current_range > 1)
local_edge_turfs = local_field_turfs - RANGE_TURFS(current_range, center)
return list(FIELD_TURFS_KEY = local_field_turfs, EDGE_TURFS_KEY = local_edge_turfs)
//Gets edge direction/corner, only works with square radius/WDH fields!
/datum/proximity_monitor/advanced/proc/get_edgeturf_direction(turf/T, turf/center_override = null)
var/turf/checking_from = get_turf(host)
if(istype(center_override))
checking_from = center_override
if(!(T in edge_turfs))
return
if(((T.x == (checking_from.x + current_range)) || (T.x == (checking_from.x - current_range))) && ((T.y == (checking_from.y + current_range)) || (T.y == (checking_from.y - current_range))))
return get_dir(checking_from, T)
if(T.x == (checking_from.x + current_range))
return EAST
if(T.x == (checking_from.x - current_range))
return WEST
if(T.y == (checking_from.y - current_range))
return SOUTH
if(T.y == (checking_from.y + current_range))
return NORTH
/datum/proximity_monitor/advanced/proc/field_turf_crossed(atom/movable/movable, turf/old_location, turf/new_location)
return
/datum/proximity_monitor/advanced/proc/field_turf_uncrossed(atom/movable/movable, turf/old_location, turf/new_location)
return
/datum/proximity_monitor/advanced/proc/field_edge_crossed(atom/movable/movable, turf/old_location, turf/new_location)
if(edge_is_a_field) // If the edge is considered a field, pass crossed to that
field_turf_crossed(movable, old_location, new_location)
/datum/proximity_monitor/advanced/proc/field_edge_uncrossed(atom/movable/movable, turf/old_location, turf/new_location)
if(edge_is_a_field) // If the edge is considered a field, pass uncrossed to that
field_turf_uncrossed(movable, old_location, new_location)
//DEBUG FIELD ITEM
/obj/item/multitool/field_debug
name = "strange multitool"
desc = "Seems to project a colored field!"
var/operating = FALSE
var/datum/proximity_monitor/advanced/debug/current = null
/obj/item/multitool/field_debug/Destroy()
QDEL_NULL(current)
return ..()
/obj/item/multitool/field_debug/proc/setup_debug_field()
current = new(src, 5, FALSE)
current.set_fieldturf_color = "#aaffff"
current.set_edgeturf_color = "#ffaaff"
current.recalculate_field(full_recalc = TRUE)
/obj/item/multitool/field_debug/attack_self(mob/user)
operating = !operating
to_chat(user, span_notice("You turn [src] [operating? "on":"off"]."))
if(!istype(current) && operating)
setup_debug_field()
else if(!operating)
QDEL_NULL(current)
/obj/item/multitool/field_debug/attack_self_secondary(mob/user, modifiers)
current.edge_is_a_field = !current.edge_is_a_field
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
//DEBUG FIELDS
/datum/proximity_monitor/advanced/debug
current_range = 5
var/set_fieldturf_color = "#aaffff"
var/set_edgeturf_color = "#ffaaff"
/datum/proximity_monitor/advanced/debug/setup_edge_turf(turf/target)
. = ..()
target.color = set_edgeturf_color
/datum/proximity_monitor/advanced/debug/cleanup_edge_turf(turf/target)
. = ..()
target.color = initial(target.color)
/datum/proximity_monitor/advanced/debug/setup_field_turf(turf/target)
. = ..()
target.color = set_fieldturf_color
/datum/proximity_monitor/advanced/debug/cleanup_field_turf(turf/target)
. = ..()
target.color = initial(target.color)
#undef FIELD_TURFS_KEY
#undef EDGE_TURFS_KEY