Files
PollardTheDragon 24845b238a The Divine Flock - DaedalusDock Flockmind Port (#31984)
* Initial commit - FLOCKMIND - Probably has like a billion things to fix

* Do after conversions

* Config

* Moved the files, icon fixes

* Tick everything, language work, event, spawn landmark, role prefs, beginning mob port

* Spans and some other fixes. Also the tickening

* More tickening

* More fixes. Lots of fixes.

* More Fixes

* A whole lot more. Also flock TGUI.

* Fixes fixes fixes fixes fixes

* FIXES

* More fixes - PR ready, still needs a fuckton of testing

* Fixes

* fix incomplete upstream merge

* fix FlockPanel + sort button name

* TGUI review

* Fixes tealprint list

* Fixes

* More fixes

* Incapacitator Fix

* Filenames

* Linters

* Interceptor range buff

* Reagent counts

* Linters

* Fabricator vendor fix

* Keybinds and HUD - Flockdrones, Fixes Vendor Conversion, Cube Materials

* Reworks reagent, adds flock grilles, fixes compute node overlays

* Intent-based flockdrone parts

* Intent based drone parts

* Radial control panel for controlling drones manually, phasing through windows/grilles

* Movement fixes

* Radio talk power, stare fix

* Flock health HUD

* Fixes flock lights, linters

* Unit tests

* Adds countdown to relay

* Relay improvements

* Small fix

* Logic Schmogic

* Relay overlay and looping sound effect

* Ignore air when converting turfs

* Cage fixes and improvements

* Improved flock bolt

* Turret conversions

* Flock bolts taze simple or basic mobs

* Sentience type

* Fixe

* Linter

* tgui review stage 2

* Concentrated Repair Burst

* Improves radio detection

* Removes extra space

* Adds healing visual effect

* Cube tech levels

* Ghooost

* Excess

* Flock doors, chairs, lattices. Centralizes conversion code.  Crafting with Gnesis

* Update code/modules/antagonists/flockmind/ai_behaviors/flock_wander.dm

Co-authored-by: Kapu1178 <75460809+Kapu1178@users.noreply.github.com>
Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>

* Fixes the fix

* Astar movement detection

* Fix, extraneous code, language stuff

* Language fixes and wander fix

* Fixes

* Another fix

* Lints

* Another linter

* Language improvement

* More language improvements

* Time requirement and appearing in orbit menu as an antag

* Cube glow

* TGUI

* Minicache

* Linters

* Grammar

* Material ID fix

* Lid fix

* Reagent turf reaction

* Reagent fix

* Butcher results

* Conversion rates

* Flock stare fix

* Fixes stare behavior

* Staring

* Flock mob blood

* Flock mobs gibs and blood. Also some runtime fixes

* Flock mobs now resist out of grabs, buckles, lockers, and more

* Fixes flock orbit, fixes a runtime I think,

* Target mechs, damage mechs, other bug fixes

* Cage fix

* Cage resist change

* Some mind changes, gatecrash buff

* Drones now shoot mechs, stare improvement

* Cut down on spam a little

* Nest fix

* No more resist spam

* Fixed drone death control

* Resist statement

* Makes the relay alarm scarier

* Fixes dead flock camera mobs having no ghost sprite, something with ghosting

* Enhanced flockphasing

* Improved flockmob pathing

* Added required turf restriction to relay

* Increased needed bandwidth for relay construction

* Nerfed drone substrate rate

* Added new status tab items for relay progress

* Another relay cost adjustment

* Improves drone AI responsiveness

* Computer frames now become flock computers

* Improves target finding for conversion, building, and replicating

* Reduced flock event pop requirements

* Adjusts flock protection on structures. Adjusts overlays.

* Relay unlock tweak

* Fixwes flock being able to gib mech'd AIs with one button

* Map conflict

* Flock can no longer be outed by merely existing

* Fied bug causing drones to shoot themselves

* Prevents mobs from attacking while in a cage

* Converter tool can now open closets and crates.

* Adds descriptions to flockdrone tools.

* More informatic blurbs

* Adds xenobiology organs

* Organ lint

* TGUI merge

* bundle and mm

---------

Signed-off-by: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
Co-authored-by: Toastical <20125180+Toastical@users.noreply.github.com>
Co-authored-by: Kapu1178 <75460809+Kapu1178@users.noreply.github.com>
Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
2026-07-08 21:31:28 +00:00

201 lines
8.3 KiB
Plaintext

/// Abstract class for an action an AI can take. Can range from movement to grabbing a nearby weapon.
/datum/ai_behavior
/// What distance you need to be from the target to perform the action.
var/required_distance = 1
/// If >0, overrides controller.target_search_radius
var/search_radius_override = null
/// Flags for extra behavior
var/behavior_flags = NONE
/// Cooldown between actions performances, defaults to the value of
/// CLICK_CD_MELEE because that seemed like a nice standard for the speed of
/// AI behavior
var/action_cooldown = CLICK_CD_MELEE
/// A multiplier applied to the behavior's goap_score().
var/goap_weight = 1
/// Behaviors to add upon a successful setup
var/list/sub_behaviors
/// Called by the AI controller when first being added. Additional arguments
/// depend on the behavior type. For example, if the behavior involves attacking
/// a mob, you may require an argument naming the blackboard key which points to
/// the target. Return FALSE to cancel.
/datum/ai_behavior/proc/setup(datum/ai_controller/controller, ...)
return TRUE
/// Returns the delay to use for this behavior in the moment. The default
/// behavior cooldown is `CLICK_CD_MELEE`, but can be customized; for example,
/// you may want a mob crawling through vents to move slowly and at a random
/// pace between pipes.
/datum/ai_behavior/proc/get_cooldown(datum/ai_controller/cooldown_for)
return action_cooldown
/// Called by the AI controller when this action is performed. This will
/// typically require consulting the blackboard for information on the specific
/// actions desired from this behavior, by passing the relevant blackboard data
/// keys to this proc. Returns a combination of [AI_BEHAVIOR_DELAY] or
/// [AI_BEHAVIOR_INSTANT], determining whether or not a cooldown occurs, and
/// [AI_BEHAVIOR_SUCCEEDED] or [AI_BEHAVIOR_FAILED]. The behavior's
/// `finish_action` proc is given TRUE or FALSE depending on whether or not the
/// return value of `perform` is marked as successful or unsuccessful.
/datum/ai_behavior/proc/perform(seconds_per_tick, datum/ai_controller/controller, ...)
SHOULD_CALL_PARENT(TRUE)
controller.behavior_cooldowns[src] = world.time + action_cooldown
/// Called when the action is finished. This needs the same args as `perform`
/// besides the default ones. This should be used to clear up the blackboard of
/// any unnecessary or obsolete data, and update the state of the pawn if
/// necessary once we know whether or not the AI action was successful.
/// `succeeded` is `TRUE` or `FALSE` depending on whether
/// [/datum/ai_behavior/proc/perform] returns [AI_BEHAVIOR_SUCCEEDED] or
/// [AI_BEHAVIOR_FAILED].
/datum/ai_behavior/proc/finish_action(datum/ai_controller/controller, succeeded, ...)
SHOULD_CALL_PARENT(TRUE)
controller.dequeue_behavior(src)
controller.behavior_args -= type
// If this was a movement task, reset our movement target if necessary
if(!(behavior_flags & AI_BEHAVIOR_REQUIRE_MOVEMENT))
next_behavior(controller, succeeded)
return
if(behavior_flags & AI_BEHAVIOR_KEEP_MOVE_TARGET_ON_FINISH)
return
clear_movement_target(controller)
controller.ai_movement.stop_moving_towards(controller)
/// Helper proc to ensure consistency in setting the source of the movement target
/datum/ai_behavior/proc/set_movement_target(datum/ai_controller/controller, atom/target, datum/ai_movement/new_movement)
controller.set_movement_target(type, target, new_movement)
/// Clear the controller's movement target only if it was us who last set it
/datum/ai_behavior/proc/clear_movement_target(datum/ai_controller/controller)
if(controller.movement_target_source != type)
return
controller.set_movement_target(type, null)
/// Returns a behavior to perform after this one, or null if continuing this one
/datum/ai_behavior/proc/next_behavior(datum/ai_controller/controller, success)
return null
/// Executed before goap_score(), to see if the behavior should even be considered.
/datum/ai_behavior/proc/goap_precondition(datum/ai_controller/controller)
return TRUE
/// Returns a numerical value that is essentially a priority for planner behaviors.
/datum/ai_behavior/proc/goap_score(datum/ai_controller/controller)
return score_distance(controller, goap_get_ideal_target(controller))
/// Returns the ideal target for this behavior.
/datum/ai_behavior/proc/goap_get_ideal_target(datum/ai_controller/controller, set_path = FALSE)
var/list/options = goap_filter_targets(controller)
return get_best_target_by_distance_score(controller, options, set_path)
/// Filter through potential targets to find real targets.
/datum/ai_behavior/proc/goap_filter_targets(datum/ai_controller/controller)
var/list/options = list()
for(var/atom/potential_target as anything in goap_get_potential_targets(controller))
if(goap_is_valid_target(controller, potential_target))
options += potential_target
return options
/// Returns a list of potential targets to filter through.
/datum/ai_behavior/proc/goap_get_potential_targets(datum/ai_controller/controller)
return list()
/// Returns TRUE if the given atom is a valid target for this behavior.
/datum/ai_behavior/proc/goap_is_valid_target(datum/ai_controller/controller, atom/target)
return TRUE
#define BINARY_INSERT_TARGET(target_list, target, score) \
do { \
var/length = length(target_list); \
if(!length) { \
target_list[target] = score; \
} else { \
var/left = 1; \
var/right = length; \
var/middle = (left + right) >> 1; \
while(left < right) { \
if(target_list[target_list[middle]] <= score) { \
left = middle + 1; \
} else { \
right = middle; \
}; \
middle = (left + right) >> 1; \
}; \
middle = target_list[target_list[middle]] > score ? middle : middle + 1; \
target_list.Insert(middle, target); \
target_list[target] = score; \
}; \
} while(FALSE)
/// Returns the best target by scoring the distance of each possible target.
/// Takes a list to insert the path into, so it can be handed back and re-used.
/datum/ai_behavior/proc/get_best_target_by_distance_score(datum/ai_controller/controller, list/targets, set_path = FALSE)
if(!length(targets))
return null
var/atom/movable/pawn = controller.pawn
var/list/access = controller.get_access()
var/list/targets_by_score = list()
var/list/reachable_targets = list()
// Sort targets by their estimated score. The last element in the lists has the highest score.
while(length(targets))
var/index = rand(1, length(targets))
var/atom/A = targets[index]
targets.Cut(index, index + 1)
var/score = score_distance(controller, A)
BINARY_INSERT_TARGET(targets_by_score, A, score)
// WEE WOO WEE WOO BEHAVIOR-CHANGING MICRO-OPT: we assume turfs further than 1 tile aren't reachable
// Because this is true in 99.9999999999999999% of cases
if(get_dist(pawn, A) <= 1 && A.Adjacent(pawn))
BINARY_INSERT_TARGET(reachable_targets, A, score)
// Go through our sorted target list until we find a path to one.
// Note: This does mean that the found target might not be the ideal one, as it's operating on the estimate
// This is a performance thing. We cannot actually use the true best target.
var/atom/ideal_atom
var/list/ideal_path
if(length(reachable_targets))
ideal_atom = reachable_targets[length(reachable_targets)]
else
while(length(targets_by_score))
var/atom/candidate = targets_by_score[length(targets_by_score)]
targets_by_score.len--
var/list/path = SSpathfinder.astar_pathfind_now(
controller.pawn,
candidate,
controller.max_target_distance,
required_distance,
access,
HAS_TRAIT(controller.pawn, TRAIT_FLYING),
)
if(path)
ideal_atom = candidate
ideal_path = path
break
if(set_path && length(ideal_path))
controller.clear_blackboard_key(BB_PATH_TO_USE)
controller.set_blackboard_key(BB_PATH_TO_USE, ideal_path)
return ideal_atom
#undef BINARY_INSERT_TARGET
/// Helper for scoring something based on the distance between it and the pawn.
/// By default, returns a value between 100 and -INFINITY, where 100 is a distance of 0 steps.
/// A distance equal to target_search_radius is zero.
/// A distance greater than target_search_radius is negative.
/datum/ai_behavior/proc/score_distance(datum/ai_controller/controller, atom/target)
var/search_radius = search_radius_override || controller.target_search_radius
if(isnull(target))
return -INFINITY
return 100 * (search_radius - get_dist_manhattan(get_turf(controller.pawn), get_turf(target))) / search_radius