Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: GOAI Urist port v2 #1414

Open
wants to merge 31 commits into
base: baymerge-testing
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0b4b230
wip: Initial file-dump into the repo. Commit for tracking deltas on t…
Mar 2, 2024
5fae411
wip: Fixing conflicts
Mar 2, 2024
b22e64a
Updated to 13.03.2024 GOAI lib
Mar 13, 2024
c93692e
chore: Moved everything under urist/ again to get it to compile corre…
Mar 13, 2024
24e681b
fix: Gave '/mob/living/carbon/human's a SmartObject actionset - basic…
Mar 14, 2024
da66baa
feature: Gave hostiles a basic ranged mob SmartObject spec - they can…
Mar 14, 2024
f2f4dcb
feature: Better tick-handling (broken up sleep()s), mass-pause, overr…
Mar 14, 2024
3ea1045
feature: Significant Coverleap improvements, BBox CF and Grenade logi…
Mar 15, 2024
6272b57
feature: Generating personalities from template files. List-ified Per…
Mar 17, 2024
683d25f
fix: Missed some files I guess
Mar 17, 2024
9786065
feature: Added a random factor to AI tickrate to stagger them - both …
Mar 20, 2024
53be77a
fix: Big pile o' stuff; fixed melee, fixed some bad raycast logic, fi…
Mar 23, 2024
b2676ca
fix: Some JSON I forgot to port from the other repo
Mar 23, 2024
cce8dbf
feature: Nigh-fully working Z-level movement! Ladders, stairs and hol…
Mar 27, 2024
e6b8cb4
feature: 3d movement fully working (aside from ladder contextfetcher …
Apr 1, 2024
589deb0
fix: Fixes to devcode version of 3d movement.
Apr 4, 2024
95c75e4
fix: Sync to latest GOAI repo; fixed runaway AI tickrates, cleaned up…
Apr 7, 2024
c9916ec
chore: First pass at linter fixes. Definitely not the last.
Apr 7, 2024
048b598
chore: Lints pt. 2
Apr 7, 2024
a7fd634
chore: Lints pt. 3 - hopefully the last of Dreamchecker ones
Apr 7, 2024
8543191
fix: Lints pt. 4: Ironing is delicious Edition
Apr 7, 2024
c726fb5
chore: Let's see if that helps with the weird indents lint
Apr 7, 2024
a9d396c
chore: Fixes some densities. Reverts the previous failed attempt to f…
Apr 7, 2024
707e899
chore: Removing old stuff. Replaced duplicate goai_data dirs with com…
Apr 7, 2024
9dde9df
chore: Squashing lints, methodically.
Apr 7, 2024
8df4b58
chore: Code quality fixes. Or adjustments, where I know for sure it's…
Apr 7, 2024
02b770b
chore: More of the same
Apr 7, 2024
88799e6
Sync to GOAI commit 9dbdff49481f1ebd89bed20c22041409ce3cfe9b
Apr 13, 2024
be6d842
feature: Adds admin verbs to self-grant GOAI verbs (including a verb …
Apr 13, 2024
5423339
fix: Lintermancy
Apr 13, 2024
43899e8
feature: Refactoring to account for Factions/non-mob AIs better.
Apr 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
144 changes: 144 additions & 0 deletions baystation12.dme
Original file line number Diff line number Diff line change
Expand Up @@ -3334,6 +3334,150 @@
#include "code\modules\urist\gamemodes\scom\shuttles.dm"
#include "code\modules\urist\gamemodes\scom\turfs&areas.dm"
#include "code\modules\urist\gamemodes\scom\weapons&armour.dm"
#include "code\modules\urist\GOAI\__goai_ss13_support.dm"
#include "code\modules\urist\GOAI\_b_debug_modules.dm"
#include "code\modules\urist\GOAI\_defines.dm"
#include "code\modules\urist\GOAI\_utility_defines.dm"
#include "code\modules\urist\GOAI\demoGoap.dm"
#include "code\modules\urist\GOAI\helpers.dm"
#include "code\modules\urist\GOAI\__ss13\_ss13macros.dm"
#include "code\modules\urist\GOAI\__ss13\_ss13stubmacros.dm"
#include "code\modules\urist\GOAI\_algorithms\astar.dm"
#include "code\modules\urist\GOAI\_algorithms\beams.dm"
#include "code\modules\urist\GOAI\_algorithms\gaussdist.dm"
#include "code\modules\urist\GOAI\_algorithms\interpolation.dm"
#include "code\modules\urist\GOAI\_algorithms\linear_algebra.dm"
#include "code\modules\urist\GOAI\_algorithms\metrics.dm"
#include "code\modules\urist\GOAI\_algorithms\raytrace.dm"
#include "code\modules\urist\GOAI\_algorithms\statistics.dm"
#include "code\modules\urist\GOAI\_algorithms\trigonometry.dm"
#include "code\modules\urist\GOAI\_algorithms\turfchunk.dm"
#include "code\modules\urist\GOAI\_datastructures\action_tracker.dm"
#include "code\modules\urist\GOAI\_datastructures\dict.dm"
#include "code\modules\urist\GOAI\_datastructures\eventqueue.dm"
#include "code\modules\urist\GOAI\_datastructures\events.dm"
#include "code\modules\urist\GOAI\_datastructures\faction_data.dm"
#include "code\modules\urist\GOAI\_datastructures\lists.dm"
#include "code\modules\urist\GOAI\_datastructures\path_tracker.dm"
#include "code\modules\urist\GOAI\_datastructures\pqueue.dm"
#include "code\modules\urist\GOAI\_datastructures\tuple.dm"
#include "code\modules\urist\GOAI\_datastructures\turfchunk.dm"
#include "code\modules\urist\GOAI\_datastructures\vectors.dm"
#include "code\modules\urist\GOAI\_datastructures\registries\ai.dm"
#include "code\modules\urist\GOAI\_datastructures\registries\aibrain.dm"
#include "code\modules\urist\GOAI\_datastructures\registries\dynamic_query_cache.dm"
#include "code\modules\urist\GOAI\_datastructures\registries\faction.dm"
#include "code\modules\urist\GOAI\_datastructures\registries\file_cache.dm"
#include "code\modules\urist\GOAI\_datastructures\registries\regex_cache.dm"
#include "code\modules\urist\GOAI\_datastructures\registries\smartobjects.dm"
#include "code\modules\urist\GOAI\basics\_cover.dm"
#include "code\modules\urist\GOAI\basics\_dir_blocker.dm"
#include "code\modules\urist\GOAI\basics\actions.dm"
#include "code\modules\urist\GOAI\basics\atom.dm"
#include "code\modules\urist\GOAI\basics\brain.dm"
#include "code\modules\urist\GOAI\basics\chunkserver.dm"
#include "code\modules\urist\GOAI\basics\chunkyAstar.dm"
#include "code\modules\urist\GOAI\basics\memory.dm"
#include "code\modules\urist\GOAI\basics\pathfinding_penalties.dm"
#include "code\modules\urist\GOAI\basics\projectile.dm"
#include "code\modules\urist\GOAI\basics\relationships.dm"
#include "code\modules\urist\GOAI\basics\senses.dm"
#include "code\modules\urist\GOAI\basics\turfs.dm"
#include "code\modules\urist\GOAI\basics\turfs_debug.dm"
#include "code\modules\urist\GOAI\basics\concrete_brains\_concrete.dm"
#include "code\modules\urist\GOAI\basics\concrete_brains\sim.dm"
#include "code\modules\urist\GOAI\goap\goap_classy.dm"
#include "code\modules\urist\GOAI\integrations\_defines.dm"
#include "code\modules\urist\GOAI\integrations\ai_state.dm"
#include "code\modules\urist\GOAI\integrations\covers.dm"
#include "code\modules\urist\GOAI\integrations\food.dm"
#include "code\modules\urist\GOAI\integrations\planner_cache.dm"
#include "code\modules\urist\GOAI\integrations\utility_plantest_tools.dm"
#include "code\modules\urist\GOAI\integrations\blocker_definitions\_shared.dm"
#include "code\modules\urist\GOAI\integrations\blocker_definitions\machinery.dm"
#include "code\modules\urist\GOAI\integrations\blocker_definitions\mecha.dm"
#include "code\modules\urist\GOAI\integrations\blocker_definitions\misc.dm"
#include "code\modules\urist\GOAI\integrations\blocker_definitions\mob.dm"
#include "code\modules\urist\GOAI\integrations\blocker_definitions\structure.dm"
#include "code\modules\urist\GOAI\integrations\cover_definitions\_shared.dm"
#include "code\modules\urist\GOAI\integrations\cover_definitions\machinery.dm"
#include "code\modules\urist\GOAI\integrations\cover_definitions\mecha.dm"
#include "code\modules\urist\GOAI\integrations\cover_definitions\mob.dm"
#include "code\modules\urist\GOAI\integrations\cover_definitions\structures.dm"
#include "code\modules\urist\GOAI\integrations\devfakes\covers.dm"
#include "code\modules\urist\GOAI\integrations\devfakes\multiz.dm"
#include "code\modules\urist\GOAI\integrations\smartobject_definitions\doors.dm"
#include "code\modules\urist\GOAI\integrations\smartobject_definitions\example_agent.dm"
#include "code\modules\urist\GOAI\integrations\smartobject_definitions\gun.dm"
#include "code\modules\urist\GOAI\integrations\smartobject_definitions\ladder.dm"
#include "code\modules\urist\GOAI\integrations\smartobject_definitions\table.dm"
#include "code\modules\urist\GOAI\integrations\spawners\_base.dm"
#include "code\modules\urist\GOAI\integrations\spawners\mob_commanders.dm"
#include "code\modules\urist\GOAI\integrations\spawners\multiz.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\_defines.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\action_handling.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\adjacency_helpers.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\brain.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\core.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\debug.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\global_planactions_repo.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\movement.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\orders.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\planning.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\relations.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\senses.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\actions\coverleap.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\actions\debug.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\actions\flee.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\actions\idle.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\actions\ladder.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\actions\move_to.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\actions\open_door.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\actions\pickup.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\actions\plan_move.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\actions\punch.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\actions\request_plan.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\actions\shoot.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\actions\tables.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\actions\wrapper.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\mob_commander\attach.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\mob_commander\combat.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\mob_commander\combat_commander.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\mob_commander\mob_commander.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\mob_commander\mob_orders.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\senses\action_senses.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\senses\pathservice.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\senses\vision.dm"
#include "code\modules\urist\GOAI\integrations\utility_agent\systems\movement_system.dm"
#include "code\modules\urist\GOAI\utility\actions.dm"
#include "code\modules\urist\GOAI\utility\actionset.dm"
#include "code\modules\urist\GOAI\utility\actiontemplate.dm"
#include "code\modules\urist\GOAI\utility\astar.dm"
#include "code\modules\urist\GOAI\utility\brain.dm"
#include "code\modules\urist\GOAI\utility\consideration_procs.dm"
#include "code\modules\urist\GOAI\utility\considerations_core.dm"
#include "code\modules\urist\GOAI\utility\context_fetchers.dm"
#include "code\modules\urist\GOAI\utility\debug.dm"
#include "code\modules\urist\GOAI\utility\response_curves.dm"
#include "code\modules\urist\GOAI\utility\serde.dm"
#include "code\modules\urist\GOAI\utility\smart_objects.dm"
#include "code\modules\urist\GOAI\utility\consideration_inputs\abstract.dm"
#include "code\modules\urist\GOAI\utility\consideration_inputs\aggregate.dm"
#include "code\modules\urist\GOAI\utility\consideration_inputs\health.dm"
#include "code\modules\urist\GOAI\utility\consideration_inputs\memory.dm"
#include "code\modules\urist\GOAI\utility\consideration_inputs\needs.dm"
#include "code\modules\urist\GOAI\utility\consideration_inputs\pathing.dm"
#include "code\modules\urist\GOAI\utility\consideration_inputs\personality.dm"
#include "code\modules\urist\GOAI\utility\consideration_inputs\planning.dm"
#include "code\modules\urist\GOAI\utility\consideration_inputs\random.dm"
#include "code\modules\urist\GOAI\utility\consideration_inputs\social.dm"
#include "code\modules\urist\GOAI\utility\consideration_inputs\spatial.dm"
#include "code\modules\urist\GOAI\utility\context_fetchers\abstract.dm"
#include "code\modules\urist\GOAI\utility\context_fetchers\bbox.dm"
#include "code\modules\urist\GOAI\utility\context_fetchers\cover.dm"
#include "code\modules\urist\GOAI\utility\context_fetchers\memory.dm"
#include "code\modules\urist\GOAI\utility\context_fetchers\shieldwall.dm"
#include "code\modules\urist\GOAI\utility\context_fetchers\spatial.dm"
#include "code\modules\urist\items\ai_modules.dm"
#include "code\modules\urist\items\ammo.dm"
#include "code\modules\urist\items\chairpainter.dm"
Expand Down
9 changes: 8 additions & 1 deletion code/modules/multiz/structures.dm
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,15 @@
return TRUE

/obj/structure/ladder/proc/climbLadder(mob/user, target_ladder, obj/item/I = null)
to_world_log("[user] is climbing [target_ladder]")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, dev changes, will revert

var/turf/T = get_turf(target_ladder)
for(var/atom/A in T)
if(A == user)
continue

if(!A.CanPass(user, user.loc, 1.5, 0))
to_chat(user, SPAN_NOTICE("\The [A] is blocking \the [src]."))
to_world_log(SPAN_NOTICE("\The [A] is blocking \the [src]."))

//We cannot use the ladder, but we probably can remove the obstruction
var/atom/movable/M = A
Expand All @@ -223,7 +228,9 @@

playsound(src, pick(climbsounds), 50)
playsound(target_ladder, pick(climbsounds), 50)
return user.Move(T)
var/result = user.Move(T)
to_world_log("[user] has climbed [target_ladder] to [T] (Result: [result])")
return result
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, dev changes, will revert


/obj/structure/ladder/CanPass(obj/mover, turf/source, height, airflow)
return airflow || !density
Expand Down
3 changes: 2 additions & 1 deletion code/modules/multiz/turf.dm
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
icon = 'icons/turf/space.dmi'
icon_state = ""
density = FALSE
pathweight = INFINITY //Seriously, don't try and path over this one numbnuts
//pathweight = INFINITY //Seriously, don't try and path over this one numbnuts
pathweight = 3 // Bay override, they dun goofed badly

z_flags = ZM_MIMIC_DEFAULTS | ZM_MIMIC_OVERWRITE | ZM_MIMIC_NO_AO | ZM_ALLOW_ATMOS

Expand Down
1 change: 1 addition & 0 deletions code/modules/urist/GOAI/__goai_library_features.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# define GOAI_LIBRARY_FEATURES TRUE
4 changes: 4 additions & 0 deletions code/modules/urist/GOAI/__goai_ss13_support.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# define GOAI_SS13_SUPPORT TRUE

// Where relevant, suppress SS13 native AI logic once GOAI is attached
# define GOAI_DELETE_SS13_AI 1
183 changes: 183 additions & 0 deletions code/modules/urist/GOAI/__ss13/_ss13macros.dm
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DEVJUNK: Feel free to ignore this. This is a port of SS13 code to the Dev repo. It's only here to make syncing between repos easier (can do copy/paste without unpicking individual files).

Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# ifdef GOAI_LIBRARY_FEATURES
// GOAI library copypasta, will be excluded at compile-time from SS13 code.

#define PUBLIC_GAME_MODE SSticker.master_mode

#define Clamp(value, low, high) (value <= low ? low : (value >= high ? high : value))
#define CLAMP01(x) (Clamp(x, 0, 1))

#define get_turf(A) get_step(A,0)

#define get_x(A) (get_step(A, 0)?.x || 0)

#define get_y(A) (get_step(A, 0)?.y || 0)

#define get_z(A) (get_step(A, 0)?.z || 0)

#define isAI(A) istype(A, /mob/living/silicon/ai)

#define isalien(A) istype(A, /mob/living/carbon/alien)

#define isanimal(A) istype(A, /mob/living/simple_animal)

#define isairlock(A) istype(A, /obj/machinery/door/airlock)

#define isatom(A) isloc(A)

#define isbrain(A) istype(A, /mob/living/carbon/brain)

#define iscarbon(A) istype(A, /mob/living/carbon)

#define iscolorablegloves(A) (istype(A, /obj/item/clothing/gloves/color)||istype(A, /obj/item/clothing/gloves/insulated)||istype(A, /obj/item/clothing/gloves/thick))

#define isclient(A) istype(A, /client)

#define iscorgi(A) istype(A, /mob/living/simple_animal/corgi)

#define is_drone(A) istype(A, /mob/living/silicon/robot/drone)

#define isEye(A) istype(A, /mob/observer/eye)

#define ishuman(A) istype(A, /mob/living/carbon/human)

#define isitem(A) istype(A, /obj/item)

#define islist(A) istype(A, /list)

#define isliving(A) istype(A, /mob/living)

#define ismouse(A) istype(A, /mob/living/simple_animal/mouse)

#define ismovable(A) istype(A, /atom/movable)

#define isnewplayer(A) istype(A, /mob/new_player)

#define isobj(A) istype(A, /obj)

#define isghost(A) istype(A, /mob/observer/ghost)

#define isobserver(A) istype(A, /mob/observer)

#define isorgan(A) istype(A, /obj/item/organ/external)

#define isstack(A) istype(A, /obj/item/stack)

#define isspace(A) istype(A, /area/space)

#define isplanet(A) istype(A, /area/planet)

#define ispAI(A) istype(A, /mob/living/silicon/pai)

#define isrobot(A) istype(A, /mob/living/silicon/robot)

#define issilicon(A) istype(A, /mob/living/silicon)

#define isslime(A) istype(A, /mob/living/carbon/slime)

#define isunderwear(A) istype(A, /obj/item/underwear)

#define isvirtualmob(A) istype(A, /mob/observer/virtual)

#define isweakref(A) istype(A, /weakref)

#define attack_animation(A) if(istype(A)) A.do_attack_animation(src)

#define isopenspace(A) istype(A, /turf/simulated/open)

#define isPlunger(A) istype(A, /obj/item/clothing/mask/plunger) || istype(A, /obj/item/device/plunger/robot)

#define sequential_id(key) uniqueness_repository.Generate(/datum/uniqueness_generator/id_sequential, key)

#define random_id(key,min_id,max_id) uniqueness_repository.Generate(/datum/uniqueness_generator/id_random, key, min_id, max_id)

#define to_chat(target, message) target << (message)
#define to_world(message) world << (message)
#define to_world_log(message) world.log << (message)
#define sound_to(target, sound) target << (sound)
#define to_file(file_entry, source_var) file_entry << (source_var)
#define from_file(file_entry, target_var) file_entry >> (target_var)
#define show_browser(target, browser_content, browser_name) target << browse(browser_content, browser_name)
#define close_browser(target, browser_name) target << browse(null, browser_name)
#define show_image(target, image) target << (image)
#define send_rsc(target, rsc_content, rsc_name) target << browse_rsc(rsc_content, rsc_name)
#define open_link(target, url) target << link(url)

#define MAP_IMAGE_PATH "nano/images/[using_map.path]/"

#define map_image_file_name(z_level) "[using_map.path]-[z_level].png"

#define RANDOM_BLOOD_TYPE pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+")

#define any2ref(x) "\ref[x]"

#define CanInteract(user, state) (CanUseTopic(user, state) == STATUS_INTERACTIVE)

#define CanDefaultInteract(user) (CanUseTopic(user, DefaultTopicState()) == STATUS_INTERACTIVE)

#define CanInteractWith(user, target, state) (target.CanUseTopic(user, state) == STATUS_INTERACTIVE)

#define CanPhysicallyInteract(user) CanInteract(user, physical_state)

#define CanPhysicallyInteractWith(user, target) CanInteractWith(user, target, physical_state)

#define QDEL_NULL_LIST(x) if(x) { for(var/y in x) { qdel(y) }}; if(x) {x.Cut(); x = null } // Second x check to handle items that LAZYREMOVE on qdel.

#define QDEL_NULL(x) if(x) { qdel(x) ; x = null }

#define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE)

#define DROP_NULL(x) if(x) { x.dropInto(loc); x = null; }

#define ARGS_DEBUG log_debug("[__FILE__] - [__LINE__]") ; for(var/arg in args) { log_debug("\t[log_info_line(arg)]") }

// Helper macros to aid in optimizing lazy instantiation of lists.
// All of these are null-safe, you can use them without knowing if the list var is initialized yet

//Picks from the list, with some safeties, and returns the "default" arg if it fails
#define DEFAULTPICK(L, default) ((istype(L, /list) && L:len) ? pick(L) : default)
// Ensures L is initailized after this point
#define LAZYINITLIST(L) if (!L) L = list()
// Sets a L back to null iff it is empty
#define UNSETEMPTY(L) if (L && !L.len) L = null
// Removes I from list L, and sets I to null if it is now empty
#define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } }
// Adds I to L, initalizing L if necessary
#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
// Insert I into L at position X, initalizing L if necessary
#define LAZYINSERT(L, I, X) if(!L) { L = list(); } L.Insert(X, I);
// Adds I to L, initalizing L if necessary, if I is not already in L
#define LAZYDISTINCTADD(L, I) if(!L) { L = list(); } L |= I;
// Sets L[A] to I, initalizing L if necessary
#define LAZYSET(L, A, I) if(!L) { L = list(); } L[A] = I;
// Reads I from L safely - Works with both associative and traditional lists.
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null)
// Reads the length of L, returning 0 if null
#define LAZYLEN(L) length(L)
// Safely checks if I is in L
#define LAZYISIN(L, I) (L ? (I in L) : FALSE)
// Null-safe L.Cut()
#define LAZYCLEARLIST(L) if(L) L.Cut()
// Reads L or an empty list if L is not a list. Note: Does NOT assign, L may be an expression.
#define SANITIZE_LIST(L) ( islist(L) ? L : list() )

// Insert an object A into a sorted list using cmp_proc (/code/_helpers/cmp.dm) for comparison.
#define ADD_SORTED(list, A, cmp_proc) if(!list.len) {list.Add(A)} else {list.Insert(FindElementIndex(A, list, cmp_proc), A)}

//Currently used in SDQL2 stuff
#define send_output(target, msg, control) target << output(msg, control)
#define send_link(target, url) target << link(url)

// Spawns multiple objects of the same type
#define cast_new(type, num, args...) if((num) == 1) { new type(args) } else { for(var/i=0;i<(num),i++) { new type(args) } }

#define FLAGS_EQUALS(flag, flags) ((flag & (flags)) == (flags))

#define JOINTEXT(X) jointext(X, null)

#define SPAN_NOTICE(X) "<span class='notice'>[X]</span>"

#define SPAN_WARNING(X) "<span class='warning'>[X]</span>"

#define SPAN_DANGER(X) "<span class='danger'>[X]</span>"

# endif