Up to date timesort to resolve list issues.

This commit is contained in:
D3athrow
2015-02-05 17:44:48 -06:00
parent fe3866f28c
commit e3746f7547
16 changed files with 169 additions and 246 deletions

View File

@@ -31,29 +31,28 @@
return "[output][and_text][input[index]]"
//Returns list element or null. Should prevent "index out of bounds" error.
proc/listgetindex(var/list/list,index)
if(istype(list) && list.len)
/proc/listgetindex(list/L, index)
if(istype(L))
if(isnum(index))
if(IsInRange(index,1,list.len))
return list[index]
else if(index in list)
return list[index]
if(IsInRange(index,1,L.len))
return L[index]
else if(index in L)
return L[index]
return
proc/islist(list/list)
if(istype(list))
/proc/islist(list/L)
if(istype(L))
return 1
return 0
//Return either pick(list) or null if list is not of type /list or is empty
proc/safepick(list/list)
if(!islist(list) || !list.len)
return
return pick(list)
/proc/safepick(list/L)
if(istype(L) && L.len)
return pick(L)
//Checks if the list is empty
proc/isemptylist(list/list)
if(!list.len)
/proc/isemptylist(list/L)
if(!L.len)
return 1
return 0
@@ -65,17 +64,20 @@ proc/isemptylist(list/list)
return 0
//Empties the list by setting the length to 0. Hopefully the elements get garbage collected
proc/clearlist(list/list)
/proc/clearlist(list/list)
if(istype(list))
list.len = 0
return
//Removes any null entries from the list
proc/listclearnulls(list/list)
if(istype(list))
while(null in list)
list -= null
return
/proc/listclearnulls(list/L)
if(istype(L))
var/i=1
for(var/thing in L)
if(thing != null)
++i
continue
L.Cut(i,i+1)
/*
* Returns list containing all the entries from first list that are not present in second.
@@ -127,21 +129,43 @@ proc/listclearnulls(list/list)
return null
//Pick a random element from the list and remove it from the list.
/proc/pick_n_take(list/listfrom)
if (listfrom.len > 0)
var/picked = pick(listfrom)
listfrom -= picked
return picked
return null
/proc/pick_n_take(list/L)
if(L.len)
var/picked = rand(1,L.len)
. = L[picked]
L.Cut(picked,picked+1) //Cut is far more efficient that Remove()
//Returns the top(last) element from the list and removes it from the list (typical stack function)
/proc/pop(list/listfrom)
if (listfrom.len > 0)
var/picked = listfrom[listfrom.len]
listfrom.len--
return picked
return null
/proc/pop(list/L)
if(L.len)
. = L[L.len]
L.len--
/proc/sorted_insert(list/L, thing, comparator)
var/pos = L.len
while(pos > 0 && call(comparator)(thing, L[pos]) > 0)
pos--
L.Insert(pos+1, thing)
// Returns the next item in a list
/proc/next_list_item(var/item, var/list/L)
var/i
i = L.Find(item)
if(i == L.len)
i = 1
else
i++
return L[i]
// Returns the previous item in a list
/proc/previous_list_item(var/item, var/list/L)
var/i
i = L.Find(item)
if(i == 1)
i = L.len
else
i--
return L[i]
/*
* Sorting
@@ -165,6 +189,7 @@ proc/listclearnulls(list/list)
L.Swap(i,rand(1,L.len))
return L
//Return a list with no duplicate entries
/proc/uniquelist(var/list/L)
var/list/K = list()
@@ -184,39 +209,12 @@ proc/listclearnulls(list/list)
//any value in a list
/proc/sortList(var/list/L, cmp=/proc/cmp_text_asc)
if(!istype(L))
return
return sortTim(L.Copy(), cmp)
//uses sortList() but uses the var's name specifically. This should probably be using mergeAtom() instead
/proc/sortNames(var/list/L, order=1)
return sortTim(L, order >= 0 ? /proc/cmp_name_asc : /proc/cmp_name_dsc)
//Mergesort: divides up the list into halves to begin the sort
/proc/sortAtom(var/list/atom/L, var/order = 1)
if(isnull(L) || L.len < 2)
return L
var/middle = L.len / 2 + 1
return mergeAtoms(sortAtom(L.Copy(0,middle)), sortAtom(L.Copy(middle)), order)
//Mergsort: does the actual sorting and returns the results back to sortAtom
/proc/mergeAtoms(var/list/atom/L, var/list/atom/R, var/order = 1)
var/Li=1
var/Ri=1
ASSERT(L)
ASSERT(R)
var/list/result = new()
while(Li <= L.len && Ri <= R.len)
var/atom/rL = L[Li]
var/atom/rR = R[Ri]
if(sorttext(rL.name, rR.name) == order)
result += L[Li++]
else
result += R[Ri++]
if(Li <= L.len)
return (result + L.Copy(Li, 0))
return (result + R.Copy(Ri, 0))
//Converts a bitfield to a list of numbers (or words if a wordlist is provided)
/proc/bitfield2list(bitfield = 0, list/wordlist)
@@ -251,104 +249,38 @@ proc/listclearnulls(list/list)
i++
return i
//Don't use this on lists larger than half a dozen or so
/proc/insertion_sort_numeric_list_ascending(var/list/L)
//world.log << "ascending len input: [L.len]"
var/list/out = list(pop(L))
for(var/entry in L)
if(isnum(entry))
var/success = 0
for(var/i=1, i<=out.len, i++)
if(entry <= out[i])
success = 1
out.Insert(i, entry)
break
if(!success)
out.Add(entry)
//world.log << " output: [out.len]"
return out
/proc/insertion_sort_numeric_list_descending(var/list/L)
//world.log << "descending len input: [L.len]"
var/list/out = insertion_sort_numeric_list_ascending(L)
//world.log << " output: [out.len]"
return reverseRange(out)
// List of lists, sorts by element[key] - for things like crew monitoring computer sorting records by name.
/proc/sortByKey(var/list/L, var/key)
if(L.len < 2)
return L
var/middle = L.len / 2 + 1
return mergeKeyedLists(sortByKey(L.Copy(0, middle), key), sortByKey(L.Copy(middle), key), key)
/proc/mergeKeyedLists(var/list/L, var/list/R, var/key)
var/Li=1
var/Ri=1
var/list/result = new()
while(Li <= L.len && Ri <= R.len)
if(sorttext(L[Li][key], R[Ri][key]) < 1)
// Works around list += list2 merging lists; it's not pretty but it works
result += "temp item"
result[result.len] = R[Ri++]
else
result += "temp item"
result[result.len] = L[Li++]
if(Li <= L.len)
return (result + L.Copy(Li, 0))
return (result + R.Copy(Ri, 0))
//Mergesort: any value in a list, preserves key=value structure
/proc/sortAssoc(var/list/L)
if(L.len < 2)
return L
var/middle = L.len / 2 + 1 // Copy is first,second-1
return mergeAssoc(sortAssoc(L.Copy(0,middle)), sortAssoc(L.Copy(middle))) //second parameter null = to end of list
/proc/mergeAssoc(var/list/L, var/list/R)
var/Li=1
var/Ri=1
var/list/result = new()
while(Li <= L.len && Ri <= R.len)
if(sorttext(L[Li], R[Ri]) < 1)
result += R&R[Ri++]
else
result += L&L[Li++]
if(Li <= L.len)
return (result + L.Copy(Li, 0))
return (result + R.Copy(Ri, 0))
/proc/find_record(field, value, list/L)
for(var/datum/data/record/R in L)
if(R.fields[field] == value)
return R
//Move a single element from position fromIndex within a list, to position toIndex
//All elements in the range [1,toIndex) before the move will be before the pivot afterwards
//All elements in the range [toIndex, L.len+1) before the move will be after the pivot afterwards
//In other words, it's as if the range [fromIndex,toIndex) have been rotated using a <<< operation common to other languages.
//fromIndex and toIndex must be in the range [1,L.len+1]
//This will preserve associations ~Carnie
/proc/moveElement(list/L, fromIndex, toIndex)
if(fromIndex == toIndex || fromIndex+1 == toIndex) //no need to move
return
if(fromIndex > toIndex)
++fromIndex
else
++toIndex
++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one
L.Insert(toIndex, null)
L.Swap(fromIndex, toIndex)
L.Cut(fromIndex, fromIndex+1)
//Move elements [fromIndex,fromIndex+len) to [toIndex,toIndex+len)
//This will preserve associations and is much faster than copying to a new list
//or checking for associative lists and manually copying elements ~Carnie
//Move elements [fromIndex,fromIndex+len) to [toIndex-len, toIndex)
//Same as moveElement but for ranges of elements
//This will preserve associations ~Carnie
/proc/moveRange(list/L, fromIndex, toIndex, len=1)
var/distance = abs(toIndex - fromIndex)
if(len > distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going
if(fromIndex < toIndex)
toIndex += len
else
fromIndex += len
if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements
if(fromIndex <= toIndex)
return //no need to move
fromIndex += len //we want to shift left instead of right
for(var/i=0, i<distance, ++i)
L.Insert(fromIndex, null)
@@ -357,8 +289,6 @@ proc/listclearnulls(list/list)
else
if(fromIndex > toIndex)
fromIndex += len
else
toIndex += len //?
for(var/i=0, i<len, ++i)
L.Insert(toIndex, null)
@@ -403,4 +333,4 @@ proc/listclearnulls(list/list)
while(start < end)
L.Swap(start++,end--)
return L
return L

View File

@@ -1,5 +1,4 @@
//These are macros used to reduce on proc calls
#define moveElement(L, fromIndex, toIndex) if((fromIndex) > (toIndex)){L.Insert((toIndex), null);L.Swap((fromIndex)+1, (toIndex));L.Cut((fromIndex)+1, (fromIndex)+2);}else{L.Insert((toIndex)+1, null);L.Swap((fromIndex), (toIndex)+1);L.Cut((fromIndex), (fromIndex)+1);}
#define fetchElement(L, i) (associative) ? L[L[i]] : L[i]
//Minimum sized sequence that will be merged. Anything smaller than this will use binary-insertion sort.
@@ -34,8 +33,8 @@ var/datum/sortInstance/sortInstance = new()
proc/timSort(start, end)
runBases.len = 0
runLens.len = 0
runBases.Cut()
runLens.Cut()
var/remaining = end - start
@@ -115,7 +114,7 @@ var/datum/sortInstance/sortInstance = new()
//in other words, find where the pivot element should go using bisection search
while(left < right)
var/mid = (left + right) >> 1 //round((left+right)/2)
if(call(cmp)(pivot, fetchElement(L,mid)) < 0)
if(call(cmp)(fetchElement(L,mid), pivot) > 0)
right = mid
else
left = mid+1
@@ -372,23 +371,23 @@ var/datum/sortInstance/sortInstance = new()
proc/mergeLo(base1, len1, base2, len2)
//ASSERT(len1 > 0 && len2 > 0 && base1 + len1 == base2)
var/cursor1 = base1
var/cursor2 = base2
//degenerate cases
if(len2 == 1)
moveElement(L, base2, base1)
moveElement(L, cursor2, cursor1)
return
if(len1 == 1)
moveElement(L, base1, base2+len2-1)
moveElement(L, cursor1, cursor2+len2)
return
//Move first element of second run
moveElement(L, base2, base1) //L[dest++] = L[cursor2++]
moveElement(L, cursor2++, cursor1++)
--len2
var/cursor1 = base1+1
var/cursor2 = base2+1
outer:
while(1)
var/count1 = 0 //# of times in a row that first run won
@@ -399,9 +398,7 @@ var/datum/sortInstance/sortInstance = new()
do
//ASSERT(len1 > 1 && len2 > 0)
if(call(cmp)(fetchElement(L,cursor2), fetchElement(L,cursor1)) < 0)
moveElement(L, cursor2, cursor1)
++cursor2
++cursor1
moveElement(L, cursor2++, cursor1++)
--len2
++count2
@@ -466,8 +463,7 @@ var/datum/sortInstance/sortInstance = new()
if(len1 == 1)
//ASSERT(len2 > 0)
moveRange(L, cursor2, cursor1, len2)
moveElement(L, cursor1, cursor2+len2)
//else
//ASSERT(len2 == 0)
@@ -477,22 +473,20 @@ var/datum/sortInstance/sortInstance = new()
proc/mergeHi(base1, len1, base2, len2)
//ASSERT(len1 > 0 && len2 > 0 && base1 + len1 == base2)
var/cursor1 = base1 + len1 - 1 //start at end of sublists
var/cursor2 = base2 + len2 - 1
//degenerate cases
if(len2 == 1)
moveElement(L, base2, base1)
return
if(len1 == 1)
moveElement(L, base1, base2+len2-1)
moveElement(L, base1, cursor2+1)
return
var/cursor1 = base1 + len1 - 1
var/cursor2 = base2 + len2 - 1
moveElement(L, cursor1, cursor2)
moveElement(L, cursor1--, cursor2-- + 1)
--len1
--cursor1
--cursor2
outer:
while(1)
@@ -503,10 +497,7 @@ var/datum/sortInstance/sortInstance = new()
do
//ASSERT(len1 > 0 && len2 > 1)
if(call(cmp)(fetchElement(L,cursor2), fetchElement(L,cursor1)) < 0)
moveElement(L, cursor1, cursor2)
--cursor1
--cursor2
moveElement(L, cursor1--, cursor2-- + 1)
--len1
++count1
@@ -533,11 +524,12 @@ var/datum/sortInstance/sortInstance = new()
count1 = len1 - gallopRight(fetchElement(L,cursor2), base1, len1, len1-1) //should cursor1 be base1?
if(count1)
cursor1 -= count1
moveRange(L, cursor1+1, cursor2+1, count1) //cursor1+1 == cursor2 by definition
cursor2 -= count1
len1 -= count1
moveRange(L, cursor1+1, cursor2+1, count1)
if(len1 == 0)
break outer
@@ -546,7 +538,7 @@ var/datum/sortInstance/sortInstance = new()
if(--len2 == 1)
break outer
count2 = len2 - gallopLeft(fetchElement(L,cursor1), base1+len1, len2, len2-1)
count2 = len2 - gallopLeft(fetchElement(L,cursor1), cursor1+1, len2, len2-1)
if(count2)
cursor2 -= count2
len2 -= count2
@@ -554,9 +546,7 @@ var/datum/sortInstance/sortInstance = new()
if(len2 <= 1)
break outer
moveElement(L, cursor1, cursor2)
--cursor1
--cursor2
moveElement(L, cursor1--, cursor2-- + 1)
--len1
if(len1 == 0)
@@ -573,7 +563,6 @@ var/datum/sortInstance/sortInstance = new()
//ASSERT(len1 > 0)
cursor1 -= len1
cursor2 -= len1
moveRange(L, cursor1+1, cursor2+1, len1)
//else
@@ -664,5 +653,4 @@ var/datum/sortInstance/sortInstance = new()
#undef MIN_GALLOP
#undef MIN_MERGE
#undef moveElement
#undef fetchElement

View File

@@ -69,7 +69,7 @@
. = "[ls[++i]]" // Make sure the initial element is converted to text.
if(l-1 & 0x01) // 'i' will always be 1 here.
. += S1 // Append 1 element if the remaining elements are not a multiple of 2.
. += "[S1]" // Append 1 element if the remaining elements are not a multiple of 2.
if(l-i & 0x02)
. = text("[][][]", ., S1, S1) // Append 2 elements if the remaining elements are not a multiple of 4.
if(l-i & 0x04)

View File

@@ -424,7 +424,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
//Orders mobs by type then by name
/proc/sortmobs()
var/list/moblist = list()
var/list/sortmob = sortAtom(mob_list)
var/list/sortmob = sortNames(mob_list)
for(var/mob/living/silicon/ai/M in sortmob)
moblist.Add(M)
for(var/mob/camera/M in sortmob)
@@ -767,16 +767,21 @@ proc/anim(turf/location as turf,target as mob|obj,a_icon,a_icon_state as text,fl
if(A.vars.Find(lowertext(varname))) return 1
else return 0
//Returns: all the areas in the world
/proc/return_areas()
var/list/area/areas = list()
//Returns sortedAreas list if populated
//else populates the list first before returning it
/proc/SortAreas()
for(var/area/A in world)
areas += A
return areas
if(A.lighting_subarea)
continue
sortedAreas.Add(A)
//Returns: all the areas in the world, sorted.
/proc/return_sorted_areas()
return sortNames(return_areas())
sortTim(sortedAreas, /proc/cmp_name_asc)
/area/proc/addSorted()
sortedAreas.Add(src)
sortTim(sortedAreas, /proc/cmp_name_asc)
//Takes: Area type as text string or as typepath OR an instance of the area.
//Returns: A list of all areas of that type in the world.
@@ -1456,4 +1461,4 @@ proc/rotate_icon(file, state, step = 1, aa = FALSE)
/proc/has_edge(obj/O as obj)
if (!O) return 0
if(O.edge) return 1
return 0
return 0

View File

@@ -72,7 +72,7 @@
"}
var/even = 0
// sort mobs
for(var/datum/data/record/t in data_core.general)
for(var/datum/data/record/t in sortRecord(data_core.general))
var/name = t.fields["name"]
var/rank = t.fields["rank"]
var/real_rank = t.fields["real_rank"]

View File

@@ -79,26 +79,12 @@ proc/process_teleport_locs()
for(var/area/AR in world)
if(istype(AR, /area/shuttle) || istype(AR, /area/syndicate_station) || istype(AR, /area/wizard_station)) continue
if(teleportlocs.Find(AR.name)) continue
var/list/turfss = get_area_turfs(AR.type)
if(!istype(turfss) || !turfss.len)
//warning("Area [AR] had no turfs!")
continue
//testing("Picking a turf from [AR]/([AR.type]) turfs length [turfss.len]")
var/turf/picked = pick(get_area_turfs(AR.type))
if (picked.z == 1)
var/turf/picked = safepick(get_area_turfs(AR.type))
if (picked && picked.z == 1)
teleportlocs += AR.name
teleportlocs[AR.name] = AR
var/not_in_order = 0
do
not_in_order = 0
if(teleportlocs.len <= 1)
break
for(var/i = 1, i <= (teleportlocs.len - 1), i++)
if(sorttext(teleportlocs[i], teleportlocs[i+1]) == -1)
teleportlocs.Swap(i, i+1)
not_in_order = 1
while(not_in_order)
sortTim(teleportlocs, /proc/cmp_text_dsc)
var/list/ghostteleportlocs = list()
@@ -108,45 +94,24 @@ proc/process_ghost_teleport_locs()
if(istype(AR, /area/turret_protected/aisat) || istype(AR, /area/derelict) || istype(AR, /area/tdome))
ghostteleportlocs += AR.name
ghostteleportlocs[AR.name] = AR
var/list/turfss = get_area_turfs(AR.type)
if(!istype(turfss) || !turfss.len)
//warning("Area [AR] had no turfs!")
continue
//testing("Picking a turf from [AR]/([AR.type]) turfs length [turfss.len]")
var/turf/picked = pick(get_area_turfs(AR.type))
if (picked.z == 1 || picked.z == 5 || picked.z == 3)
var/turf/picked = safepick(get_area_turfs(AR.type))
if (picked && (picked.z == 1 || picked.z == 5 || picked.z == 3))
ghostteleportlocs += AR.name
ghostteleportlocs[AR.name] = AR
var/not_in_order = 0
do
not_in_order = 0
if(ghostteleportlocs.len <= 1)
break
for(var/i = 1, i <= (ghostteleportlocs.len - 1), i++)
if(sorttext(ghostteleportlocs[i], ghostteleportlocs[i+1]) == -1)
ghostteleportlocs.Swap(i, i+1)
not_in_order = 1
while(not_in_order)
sortTim(ghostteleportlocs, /proc/cmp_text_dsc)
var/global/list/adminbusteleportlocs = list()
proc/process_adminbus_teleport_locs()
for(var/area/AR in world)
if(adminbusteleportlocs.Find(AR.name)) continue
adminbusteleportlocs += AR.name
adminbusteleportlocs[AR.name] = AR
var/turf/picked = safepick(get_area_turfs(AR.type))
if (picked)
adminbusteleportlocs += AR.name
adminbusteleportlocs[AR.name] = AR
var/not_in_order = 0
do
not_in_order = 0
if(adminbusteleportlocs.len <= 1)
break
for(var/i = 1, i <= (adminbusteleportlocs.len - 1), i++)
if(sorttext(adminbusteleportlocs[i], adminbusteleportlocs[i+1]) == -1)
adminbusteleportlocs.Swap(i, i+1)
not_in_order = 1
while(not_in_order)
sortTim(adminbusteleportlocs, /proc/cmp_text_dsc)
/*-----------------------------------------------------------------------------*/

View File

@@ -117,7 +117,7 @@
//crewmembers += "temporary item"
//crewmembers[crewmembers.len] = crewmemberData
crewmembers = sortByKey(crewmembers, "name")
crewmembers = sortList(crewmembers)
data["crewmembers"] = crewmembers

View File

@@ -265,7 +265,7 @@
else
user << "<span class='notice'>\The [src] smartly refuses [O].</span>"
return 1
sortList(item_quants)
updateUsrDialog()
/obj/machinery/smartfridge/attack_paw(mob/user as mob)

View File

@@ -132,6 +132,7 @@ move an amendment</a> to the drawing.</p>
A.power_environ = 0
A.always_unpowered = 0
A.SetDynamicLighting()
A.addSorted()
spawn() move_turfs_to_area(turfs, A)
A.always_unpowered = 0

View File

@@ -21,7 +21,7 @@ var/global/list/camera_bugs = list()
for (var/obj/item/device/camera_bug/C in cameras)
friendly_cameras.Add(C.c_tag)
var/target = input("Select the camera to observe", null) as null|anything in friendly_cameras
var/target = input("Select the camera to observe", null) as null|anything in sortList(friendly_cameras)
if (!target)
user.unset_machine()
user.reset_view(user)

View File

@@ -367,4 +367,7 @@ var/global/list/plugins = list()
var/global/datum/gas_mixture/space_gas = new
//Announcement intercom
var/obj/item/device/radio/intercom/announcement_intercom = new(null)
var/obj/item/device/radio/intercom/announcement_intercom = new(null)
//used by jump-to-area etc. Updated by area/updateName()
var/list/sortedAreas = list()

View File

@@ -247,6 +247,7 @@ var/list/admin_verbs_hideable = list(
/client/proc/cmd_debug_tog_aliens,
/client/proc/air_report,
/client/proc/enable_debug_verbs,
/client/proc/mob_list()
/proc/possess,
/proc/release
)

View File

@@ -1,4 +1,4 @@
/client/proc/Jump(var/area/A in return_sorted_areas())
/client/proc/Jump(var/area/A in sortedAreas)
set name = "Jump to Area"
set desc = "Area to jump to"
set category = "Admin"
@@ -7,10 +7,21 @@
return
if(config.allow_admin_jump)
var/list/L = get_area_turfs(A)
if(!L || !L.len)
if(!A)
return
usr.loc = pick(get_area_turfs(A))
var/list/turfs = list()
for(var/area/Ar in A.related)
for(var/turf/T in Ar)
if(T.density)
continue
turfs.Add(T)
var/turf/T = pick_n_take(turfs)
if(!T)
src << "Nowhere to jump to!"
return
usr.loc = T
log_admin("[key_name(usr)] jumped to [A]")
message_admins("[key_name_admin(usr)] jumped to [A]", 1)
@@ -148,7 +159,7 @@
if(!src.holder)
src << "Only administrators may use this command."
return
var/area/A = input(usr, "Pick an area.", "Pick an area") in return_sorted_areas()
var/area/A = input(usr, "Pick an area.", "Pick an area") in sortedAreas
if(A)
if(config.allow_admin_jump)
M.loc = pick(get_area_turfs(A))

View File

@@ -1258,3 +1258,20 @@ client/proc/delete_all_adminbus()
for(var/obj/structure/stool/bed/chair/vehicle/adminbus/AB in world)
AB.Adminbus_Deletion()
client/proc/mob_list()
set name = "show mob list"
set category = "Debug"
if(!holder) return
usr << "mob list length is [mob_list.len]"
var/foundnull = 0
for(var/mob/V in mob_list)
var/msg = "mob ([V]) is in slot [mob_list.Find(V)]"
if(!ismob(V))
if(isnull(V))
foundnull++
msg = "Non mob found in mob list [isnull(V) ? "null entry found at mob_list.Find(V)" : "[V]'s type is [V.type]"]"
usr << msg
if(foundnull)
usr << "Found [foundnull] null entries in the mob list, running null clearer."
listclearnulls(mob_list)

View File

@@ -51,6 +51,7 @@ USE THIS CHEMISTRY DISPENSER FOR MAPS SO THEY START AT 100 ENERGY
)
RefreshParts()
dispensable_reagents = sortList(dispensable_reagents)
/obj/machinery/chem_dispenser/RefreshParts()
var/T = 0

View File

@@ -128,9 +128,10 @@
plugins[P.name] = P
P.on_world_loaded()
process_teleport_locs() //Sets up the wizard teleport locations
process_ghost_teleport_locs() //Sets up ghost teleport locations.
process_teleport_locs() //Sets up the wizard teleport locations
process_ghost_teleport_locs() //Sets up ghost teleport locations.
process_adminbus_teleport_locs() //Sets up adminbus teleport locations.
SortAreas() //Build the list of all existing areas and sort it alphabetically
spawn(3000) //so we aren't adding to the round-start lag
if(config.ToRban)