Skip to content

Commit

Permalink
SSchunks round 3. (#8213)
Browse files Browse the repository at this point in the history
* step 1

* E

* Update jamming.dm

* t

* d

* Containerization Stuff

* inventory management improvements and signal management imrprovement

* it works , it all works

* fix this!

* e

* Update jamming.dmi

* D

* eeee

* it is done

* a

* Update devices and tools.dm

* 1

* e

* Update device.dmi

* fix  + multi Z support yeaah!!

* Update code/game/objects/items.dm

Co-authored-by: Pink-Chink <[email protected]>

* fix descs

* t

* Update cev_eris.dme

* Update cev_eris.dme

* d

* Update jamming.dm

* Revert "Update jamming.dm"

This reverts commit 424ae3f.

* Revert "d"

This reverts commit dcd82b9.

* Revert "Update cev_eris.dme"

This reverts commit 13dfa02.

* Revert "Update cev_eris.dme"

This reverts commit 573ba02.

* Revert "t"

This reverts commit 8a420c6.

* Revert "Revert "t""

This reverts commit 9737982.

* Revert "Revert "Update cev_eris.dme""

This reverts commit fce0546.

* Revert "Revert "Update cev_eris.dme""

This reverts commit b9e26b9.

* Revert "Revert "d""

This reverts commit d1f74f0.

* Revert "Revert "Update jamming.dm""

This reverts commit 574cea3.

* Revert "Update jamming.dm"

This reverts commit 424ae3f.

* Revert "Revert "Update jamming.dm""

This reverts commit 069f3be.

* it works

* Functioning prototype for mobs

* fix chunk hashing + fix humans not getting registered

* Update chunks.dm

* how many linter errors can we fit in one line

* Update jamming.dm

* f

* Fix double-chunk additions and  stuff not  using forcemOve

* optimizations

* almost done with hearers

* better

* it all works

* remove all debug messages and fix hearers

* Update chunks.dm

* adjust

* fix runtimes at roundstart

* fix

* fix runtime + more performance imrpovements on visiblem essages

* fix the funny

* Update chunks.dm

* Update chunks.dm

* le z suport

* z-levels support 2

* Update chunks.dm

* Update chunks.dm

* Fix cases of null/non-existing mobs being checked.

* Fix roaches attacking from inside lockers

* Update _inventory.dm

* Update _inventory.dm

* Update jamming.dm

* Update jamming.dm

* switch to a define whose FUCKING TARGET WON'T DISSAPEAR MID EXECUTION

* zz

* Update chunks.dm

* fix item bug

* Remove debug procs and functions

---------

Co-authored-by: Pink-Chink <[email protected]>
  • Loading branch information
MLGTASTICa and BigHatLogan-dev committed Jun 30, 2023
1 parent 372408b commit 41ab997
Show file tree
Hide file tree
Showing 46 changed files with 482 additions and 139 deletions.
1 change: 1 addition & 0 deletions cev_eris.dme
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@
#include "code\controllers\subsystems\atoms.dm"
#include "code\controllers\subsystems\chat.dm"
#include "code\controllers\subsystems\chemistry.dm"
#include "code\controllers\subsystems\chunks.dm"
#include "code\controllers\subsystems\craft.dm"
#include "code\controllers\subsystems\dcs.dm"
#include "code\controllers\subsystems\economy.dm"
Expand Down
8 changes: 6 additions & 2 deletions code/__DEFINES/dcs/signals.dm
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
#define COMSIG_GLOB_FABRIC_NEW "!fabric_new" //(image/fabric)

//////////////////////////////////////////////////////////////////
// world signals

#define COMSIG_WORLD_MAXZ_INCREMENTING "world_maxz_increase"
// /datum signals
/// when a component is added to a datum: (/datum/component)
#define COMSIG_COMPONENT_ADDED "component_added"
Expand Down Expand Up @@ -60,14 +62,16 @@
#define COMSIG_TURF_LEVELUPDATE "turf_levelupdate" //levelupdate()

// /atom/movable signals
#define COMSIG_MOVABLE_MOVED "movable_moved" //from base of atom/movable/Moved(): (/atom, origin_loc, new_loc)
#define COMSIG_MOVABLE_Z_CHANGED "movable_z_moved" //from base of atom/movable/onTransitZ(): (oldz, newz)
// These 2 can be sent at the same time togheter, if you only care about the Z-level , only use the Z-changed , else only use the moved.
#define COMSIG_MOVABLE_MOVED "movable_moved" //from atom/movable/Move and forceMove: (/atom, origin_loc, new_loc)
#define COMSIG_MOVABLE_Z_CHANGED "movable_z_moved" //from atom/movable/Move and forceMove): (oldz, newz)
#define COMSIG_MOVABLE_PREMOVE "moveable_boutta_move"

// /mob signals
#define COMSIG_MOB_LIFE "mob_life" //from mob/Life()
#define COMSIG_MOB_LOGIN "mob_login" //from mob/Login()
#define COMSIG_MOB_DEATH "mob_death" //from mob/death()
#define COMSIG_MOB_INITIALIZED "mob_initialized"
#define COMSIG_SHIFTCLICK "shiftclick" // used for ai_like_control component
#define COMSIG_CTRLCLICK "ctrlclick" // used for ai_like_control component
#define COMSIG_ALTCLICK "altclick" // used for ai_like_control component
Expand Down
2 changes: 2 additions & 0 deletions code/__DEFINES/maths.dm
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

#define CEILING(x, y) ( -round(-(x) / (y)) * (y) )

#define DIST_EUCLIDIAN(x1,y1,x2,y2) (sqrt((x1-x2)**2 + (y1-y2)**2))

// round() acts like floor(x, 1) by default but can't handle other values
#define FLOOR(x, y) ( round((x) / (y)) * (y) )

Expand Down
3 changes: 2 additions & 1 deletion code/__DEFINES/subsystems.dm
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@
// The numbers just define the ordering, they are meaningless otherwise.

#define INIT_ORDER_GARBAGE 99
#define INIT_ORDER_EXPLOSIONS 98
#define INIT_ORDER_CHUNKS 98
#define INIT_ORDER_EXPLOSIONS 97
#define INIT_ORDER_SKYBOX 20
#define INIT_ORDER_DBCORE 19
#define INIT_ORDER_BLACKBOX 18
Expand Down
19 changes: 9 additions & 10 deletions code/__HELPERS/game.dm
Original file line number Diff line number Diff line change
Expand Up @@ -159,19 +159,18 @@
objs[AM] = TRUE
hearturfs[AM.locs[1]] = TRUE

for(var/m in GLOB.player_list)
var/mob/M = m
if(checkghosts == GHOSTS_ALL_HEAR && M.stat == DEAD && !isnewplayer(M) && (M.client && M.get_preference_value(/datum/client_preference/ghost_ears) == GLOB.PREF_ALL_SPEECH))
if (!mobs[M])
mobs[M] = TRUE
continue
if(M.loc && hearturfs[M.locs[1]])
if (!mobs[M])
mobs[M] = TRUE

for(var/mob/M as anything in getMobsInRangeChunked(T, range, FALSE, TRUE))
mobs[M] = TRUE
for(var/mob/M as anything in GLOB.player_ghost_list)
if(checkghosts == GHOSTS_ALL_HEAR && M.stat == DEAD && M.get_preference_value(/datum/client_preference/ghost_ears) == GLOB.PREF_ALL_SPEECH)
mobs[M] = TRUE

objs |= getHearersInRangeChunked(T, range)
/*
for(var/obj in GLOB.hearing_objects)
if(get_turf(obj) in hearturfs)
objs |= obj
*/


/proc/get_mobs_in_radio_ranges(list/obj/item/device/radio/radios)
Expand Down
1 change: 1 addition & 0 deletions code/__HELPERS/global_lists.dm
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ GLOBAL_LIST_EMPTY(ships) // List of ships in the game.

GLOBAL_LIST_EMPTY(mob_list) //EVERY single mob, dead or alive
GLOBAL_LIST_EMPTY(player_list) //List of all mobs **with clients attached**. Excludes /mob/new_player
GLOBAL_LIST_EMPTY(player_ghost_list) // List of all ghosts with an connected client.
GLOBAL_LIST_EMPTY(human_mob_list) //List of all human mobs and sub-types, including clientless
GLOBAL_LIST_EMPTY(silicon_mob_list) //List of all silicon mobs, including clientless
GLOBAL_LIST_EMPTY(living_mob_list) //List of all alive mobs, including clientless. Excludes /mob/new_player
Expand Down
257 changes: 257 additions & 0 deletions code/controllers/subsystems/chunks.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
#define CHUNK_SIZE 8
//#define CHUNKID(x,y,size) round((((x - x%size) + (y - y%size) * world.maxx) / size ** 2))
#define CHUNKID(x,y) max(1,round(x/CHUNK_SIZE)+round(y/CHUNK_SIZE)*round(world.maxx / CHUNK_SIZE))
#define CHUNKSPERLEVEL(x,y) round(world.maxx * world.maxy) / (CHUNK_SIZE ** 2) + round(world.maxx / CHUNK_SIZE)
#define CHUNKCOORDCHECK(x,y) (x > world.maxx || y > world.maxy || x <= 0 || y <= 0)
/// This subsystem is meant for anything that should not be employing byond view() and is generally very constraining to keep track of
/// For now it only has mobs and hearers, but it should also include sanity , signal receivers , and anything that is very frequently
// searched

/datum/chunk
var/list/mob/mobs = list()
//var/list/sanity_damagers = list()
var/list/obj/hearers = list()
//var/list/signal_receivers = list()

SUBSYSTEM_DEF(chunks)
name = "Chunks"
init_order = INIT_ORDER_CHUNKS
flags = SS_NO_FIRE
var/list/datum/chunk/chunk_list_by_zlevel

/datum/controller/subsystem/chunks/Initialize(timeofday)
chunk_list_by_zlevel = new/list(world.maxz)
for(var/i = 1, i <= world.maxz,i++)
chunk_list_by_zlevel[i] = new/list(CHUNKSPERLEVEL(world.maxx, world.maxy))
for(var/j = 1, j <= CHUNKSPERLEVEL(world.maxx, world.maxy), j++)
chunk_list_by_zlevel[i][j] = new /datum/chunk(src)
RegisterSignal(SSdcs, COMSIG_MOB_INITIALIZED, PROC_REF(onMobNew))
RegisterSignal(SSdcs, COMSIG_WORLD_MAXZ_INCREMENTING, PROC_REF(beforeLevelIncrement))
return ..()

/datum/controller/subsystem/chunks/proc/beforeLevelIncrement(datum/source)
SIGNAL_HANDLER
var/temp_list = new/list(world.maxz + 1)
for(var/i = 1; i <= world.maxz; i++)
temp_list[i] = chunk_list_by_zlevel[i]

temp_list[world.maxz + 1] = new/list(CHUNKSPERLEVEL(world.maxx, world.maxy))
for(var/j = 1, j <= CHUNKSPERLEVEL(world.maxx, world.maxy), j++)
temp_list[world.maxz + 1][j] = new /datum/chunk(src)
chunk_list_by_zlevel = temp_list


/datum/controller/subsystem/chunks/proc/onMobNew(atom/signalSource, mob/source)
SIGNAL_HANDLER
source.InitiateChunkTracking()

// Get mobs in range using chunks
/proc/getMobsInRangeChunked(atom/source, range, aliveonly = FALSE, canseeonly = FALSE)
if(!source || !range)
return
var/atom/container = source.getContainingAtom()
var/list/returnValue = list()
if(container.z == 0)
return returnValue
var/coordinates = list(container.x - range - CHUNK_SIZE, container.y - range - CHUNK_SIZE, container.x + range + CHUNK_SIZE, container.y + range + CHUNK_SIZE)
if(coordinates[1] == 0)
coordinates[1] = 1
if(coordinates[2] == 0)
coordinates[2] = 1
if(coordinates[3] > world.maxx)
coordinates[3] = world.maxx
if(coordinates[4] > world.maxy)
coordinates[4] = world.maxy
var/datum/chunk/chunkReference
var/turf/containerTurf = get_turf(container)
if(containerTurf == null)
return returnValue
for(var/chunkX = coordinates[1], chunkX <= coordinates[3], chunkX += CHUNK_SIZE)
for(var/chunkY = coordinates[2], chunkY <= coordinates[4], chunkY += CHUNK_SIZE)
chunkReference = SSchunks.chunk_list_by_zlevel[container.z][CHUNKID(chunkX, chunkY)]
for(var/mob/mobToCheck as anything in chunkReference.mobs)
var/turf/mobTurf = get_turf(mobToCheck)
if(!mobTurf)
continue
if(DIST_EUCLIDIAN(containerTurf.x, containerTurf.y, mobTurf.x, mobTurf.y) < range)
if(aliveonly && mobToCheck.stat == DEAD)
continue
if(canseeonly && !can_see(containerTurf, get_turf(mobToCheck), range * 2))
continue
returnValue += mobToCheck
return returnValue

/proc/getHearersInRangeChunked(atom/source, range)
if(!source || !range)
return
var/atom/container = source.getContainingAtom(source)
var/list/returnValue = list()
if(container.z == 0)
return returnValue
// IF THE RANGE IS SMALLER THAN CHUNK_SIZE , theres a risk of not checking all relevant chunks (If anyone can figure the true underlying cause to this, then feel free to remove this)
// as it'd basically just improve performance (not like its not improved enough already tho)
var/coordinates = list(container.x - range - CHUNK_SIZE, container.y - range - CHUNK_SIZE, container.x + range + CHUNK_SIZE, container.y + range + CHUNK_SIZE)
if(coordinates[1] == 0)
coordinates[1] = 1
if(coordinates[2] == 0)
coordinates[2] = 1
if(coordinates[3] > world.maxx)
coordinates[3] = world.maxx
if(coordinates[4] > world.maxy)
coordinates[4] = world.maxy
var/datum/chunk/chunkReference
var/turf/containerTurf = get_turf(container)
if(containerTurf == null)
return returnValue
for(var/chunkX = coordinates[1], chunkX <= coordinates[3], chunkX += CHUNK_SIZE)
for(var/chunkY = coordinates[2], chunkY <= coordinates[4], chunkY += CHUNK_SIZE)
chunkReference = SSchunks.chunk_list_by_zlevel[container.z][CHUNKID(chunkX, chunkY)]
for(var/obj/hearerToCheck as anything in chunkReference.hearers)
var/turf/hearerTurf = get_turf(hearerToCheck)
if(!hearerTurf)
continue
if(DIST_EUCLIDIAN(containerTurf.x, containerTurf.y, hearerTurf.x, hearerTurf.y) < range)
if(!can_see(source, get_turf(hearerToCheck), range * 2))
continue
returnValue += hearerToCheck
return returnValue

/// Mob tracking and handling
/mob/proc/chunkOnMove(atom/source, atom/oldLocation, atom/newLocation)
SIGNAL_HANDLER
var/datum/chunk/chunk_reference
if(oldLocation?.z && newLocation?.z)
if(CHUNKID(oldLocation.x, oldLocation.y) == CHUNKID(newLocation.x, newLocation.y) && oldLocation.z == newLocation.z)
return
chunk_reference = SSchunks.chunk_list_by_zlevel[oldLocation.z][CHUNKID(oldLocation.x, oldLocation.y)]
chunk_reference.mobs -= src
//if(ishuman(src))
// message_admins("[src] removed from chunkID : [CHUNKID(oldLocation.x, oldLocation.y)]")
if(CHUNKCOORDCHECK(newLocation.x, newLocation.y))
return
chunk_reference = SSchunks.chunk_list_by_zlevel[newLocation.z][CHUNKID(newLocation.x, newLocation.y)]
chunk_reference.mobs += src
//if(ishuman(src))
// message_admins("[src] added to chunkID : [CHUNKID(newLocation.x, newLocation.y)]")
else if(newLocation?.z)
if(CHUNKCOORDCHECK(newLocation.x, newLocation.y))
return
chunk_reference = SSchunks.chunk_list_by_zlevel[newLocation.z][CHUNKID(newLocation.x, newLocation.y)]
chunk_reference.mobs += src
//if(ishuman(src))
// message_admins("[src] added to chunkID : [CHUNKID(newLocation.x, newLocation.y)]")
else if(oldLocation?.z)
chunk_reference = SSchunks.chunk_list_by_zlevel[oldLocation.z][CHUNKID(oldLocation.x, oldLocation.y)]
chunk_reference.mobs -= src
//if(ishuman(src))
// message_admins("[src] removed from chunkID : [CHUNKID(oldLocation.x, oldLocation.y)]")

/mob/proc/chunkOnContainerization(atom/source, atom/newContainer , atom/oldContainer)
SIGNAL_HANDLER
//message_admins("[src] switched container from [oldContainer] to [newContainer]")
UnregisterSignal(oldContainer , COMSIG_MOVABLE_MOVED)
RegisterSignal(newContainer, COMSIG_MOVABLE_MOVED, PROC_REF(chunkOnMove))


/mob/proc/chunkClearSelf(atom/source)
SIGNAL_HANDLER
var/atom/container = getContainingAtom()
// in this case we never registered
if(container.z != 0)
var/datum/chunk/chunk_reference = SSchunks.chunk_list_by_zlevel[container.z][CHUNKID(container.x, container.y)]
chunk_reference.mobs -= src

/// Hearer tracking and handling
/obj/proc/chunkHearerOnMove(atom/source, atom/oldLocation , atom/newLocation)
SIGNAL_HANDLER
var/datum/chunk/chunk_reference
if(oldLocation?.z && newLocation?.z)
if(CHUNKID(oldLocation.x, oldLocation.y) == CHUNKID(newLocation.x, newLocation.y) && oldLocation.z == newLocation.z)
return
chunk_reference = SSchunks.chunk_list_by_zlevel[oldLocation.z][CHUNKID(oldLocation.x, oldLocation.y)]
chunk_reference.hearers -= src
//if(ishuman(src))
// message_admins("[src] removed from chunkID : [CHUNKID(oldLocation.x, oldLocation.y)]")
if(CHUNKCOORDCHECK(newLocation.x, newLocation.y))
return
chunk_reference = SSchunks.chunk_list_by_zlevel[newLocation.z][CHUNKID(newLocation.x, newLocation.y)]
chunk_reference.hearers += src
//if(ishuman(src))
// message_admins("[src] added to chunkID : [CHUNKID(newLocation.x, newLocation.y)]")
else if(newLocation?.z)
if(CHUNKCOORDCHECK(newLocation.x, newLocation.y))
return
chunk_reference = SSchunks.chunk_list_by_zlevel[newLocation.z][CHUNKID(newLocation.x, newLocation.y)]
chunk_reference.hearers += src
//if(ishuman(src))
// message_admins("[src] added to chunkID : [CHUNKID(newLocation.x, newLocation.y)]")
else if(oldLocation?.z)
chunk_reference = SSchunks.chunk_list_by_zlevel[oldLocation.z][CHUNKID(oldLocation.x, oldLocation.y)]
chunk_reference.hearers -= src
//if(ishuman(src))
// message_admins("[src] removed from chunkID : [CHUNKID(oldLocation.x, oldLocation.y)]")
/*
var/datum/chunk/chunk_reference
if(oldLocation && oldLocation.z != 0)
if(newLocation)
if(CHUNKID(oldLocation.x, oldLocation.y) == CHUNKID(newLocation.x, newLocation.y))
return
chunk_reference = SSchunks.chunk_list_by_zlevel[oldLocation.z][CHUNKID(oldLocation.x, oldLocation.y)]
chunk_reference.hearers -= src
//if(ishuman(src))
// message_admins("[src] removed from chunkID : [CHUNKID(oldLocation.x, oldLocation.y)]")
// The new location has invalid coordinates , so lets get rid of them from the old chunk and not update to another one
if(newLocation && newLocation.z != 0)
if(CHUNKCOORDCHECK(newLocation.x, newLocation.y))
return
chunk_reference = SSchunks.chunk_list_by_zlevel[newLocation.z][CHUNKID(newLocation.x, newLocation.y)]
chunk_reference.hearers += src
//if(ishuman(src))
// message_admins("[src] added to chunkID : [CHUNKID(newLocation.x, newLocation.y)]")
*/

/obj/proc/chunkHearerOnContainerization(atom/source, atom/newContainer, atom/oldContainer)
SIGNAL_HANDLER
//message_admins("[src] switched container from [oldContainer] to [newContainer]")
UnregisterSignal(oldContainer , COMSIG_MOVABLE_MOVED)
RegisterSignal(newContainer, COMSIG_MOVABLE_MOVED, PROC_REF(chunkHearerOnMove))

/obj/proc/chunkHearerClearSelf(datum/source)
var/atom/container = getContainingAtom()
UnregisterSignal(container, COMSIG_MOVABLE_MOVED)
UnregisterSignal(src, list(COMSIG_ATOM_CONTAINERED, COMSIG_PARENT_QDELETING))
// in this case we never registered
if(container.z != 0)
var/datum/chunk/chunk_reference = SSchunks.chunk_list_by_zlevel[container.z][CHUNKID(container.x, container.y)]
chunk_reference.hearers -= src
SIGNAL_HANDLER


// This is done by the mob itself because keeping track of them with reference solving is trash and unefficient
// Especially resolving references between container > contained mob to update X mob's chunk
// TGMC Minimaps are a good example (old ones they fixed their stuff)
/mob/proc/InitiateChunkTracking()
SIGNAL_HANDLER
// No to this!!
if(isnewplayer(src))
return
var/atom/highestContainer = getContainingAtom()
RegisterSignal(highestContainer, COMSIG_MOVABLE_MOVED, PROC_REF(chunkOnMove))
RegisterSignal(src, COMSIG_ATOM_CONTAINERED, PROC_REF(chunkOnContainerization))
RegisterSignal(src, COMSIG_PARENT_QDELETING, PROC_REF(chunkClearSelf))
if(highestContainer.z != 0)
var/datum/chunk/chunk_reference = SSchunks.chunk_list_by_zlevel[highestContainer.z][CHUNKID(highestContainer.x, highestContainer.y)]
chunk_reference.mobs += src

/obj/proc/InitiateHearerTracking()
var/atom/highestContainer = getContainingAtom()
RegisterSignal(highestContainer, COMSIG_MOVABLE_MOVED, PROC_REF(chunkHearerOnMove))
RegisterSignal(src, COMSIG_ATOM_CONTAINERED, PROC_REF(chunkHearerOnContainerization))
RegisterSignal(src, COMSIG_PARENT_QDELETING, PROC_REF(chunkHearerClearSelf))
if(highestContainer.z != 0)
var/datum/chunk/chunk_reference = SSchunks.chunk_list_by_zlevel[highestContainer.z][CHUNKID(highestContainer.x, highestContainer.y)]
chunk_reference.hearers += src




4 changes: 2 additions & 2 deletions code/controllers/subsystems/garbage.dm
Original file line number Diff line number Diff line change
Expand Up @@ -330,12 +330,12 @@ SUBSYSTEM_DEF(garbage)
I.qdels++

if(isnull(D.gc_destroyed))
if (SEND_SIGNAL_OLD(D, COMSIG_PARENT_PREQDELETED, force)) // Give the components a chance to prevent their parent from being deleted
if (SEND_SIGNAL(D, COMSIG_PARENT_PREQDELETED, force)) // Give the components a chance to prevent their parent from being deleted
return
D.gc_destroyed = GC_CURRENTLY_BEING_QDELETED
var/start_time = world.time
var/start_tick = world.tick_usage
SEND_SIGNAL_OLD(D, COMSIG_PARENT_QDELETING, force) // Let the (remaining) components know about the result of Destroy
SEND_SIGNAL(D, COMSIG_PARENT_QDELETING, force) // Let the (remaining) components know about the result of Destroy
var/hint = D.Destroy(arglist(args.Copy(2))) // Let our friend know they're about to get fucked up.
if(world.time != start_time)
I.slept_destroy++
Expand Down
5 changes: 3 additions & 2 deletions code/controllers/subsystems/jamming.dm
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ SUBSYSTEM_DEF(jamming)
for(var/thing in active_jammers[location.z])
// blame linters being shit
var/datum/component/jamming/jammer = thing
var/distance = get_dist_euclidian(jammer.highest_container, location)
var/radius = jammer.radius - abs((jammer.highest_container.z - location.z) * jammer.z_reduction)
var/atom/container = jammer.owner.getContainingAtom()
var/distance = DIST_EUCLIDIAN(container.x, container.y, location.x, location.y)
var/radius = jammer.radius - (abs(container.z - location.z) * jammer.z_reduction)
// incase its multi-Z jammer with distance reduction
if(distance > radius)
continue
Expand Down
Loading

0 comments on commit 41ab997

Please sign in to comment.