mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2025-12-09 16:43:51 +00:00
__HELPERS Cleaning and other things I decided to do. (#4584)
* Schizoposting * The Crungly * Tabbin' the JSON y'all * Strings
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
## Citadel Station 13 RP
|
||||
# Citadel Station 13 RP
|
||||
|
||||
[](https://github.com/Citadel-Station-13/Citadel-Station-13-RP/actions?query=workflow%3A%22CI+Suite%22)
|
||||
[](https://isitmaintained.com/project/Citadel-Station-13/Citadel-Station-13-RP "Percentage of issues still open")
|
||||
@@ -18,9 +18,10 @@ This is the codebase for the CitadelRP flavoured fork of SpaceStation 13.
|
||||
Citadel Station 13 RP, also known as CitadelRP was originally a fork of VOREStation, which separated on 01/25/2019.
|
||||
|
||||
## DOWNLOADING
|
||||
|
||||
[Downloading](.github/guides/DOWNLOADING.md)
|
||||
|
||||
## :exclamation: How to compile :exclamation:
|
||||
## :exclamation: How to compile :exclamation
|
||||
|
||||
On **May 9, 2022** we have changed the way to compile the codebase.
|
||||
|
||||
@@ -33,6 +34,7 @@ On **May 9, 2022** we have changed the way to compile the codebase.
|
||||
**[How to compile in VSCode and other build options](tools/build/README.md).**
|
||||
|
||||
## Contributors
|
||||
|
||||
[Guides for Contributors](.github/CONTRIBUTING.md)
|
||||
|
||||
[CitadelRP HACKMD account](https://hackmd.io/@CitadelStation13RP)
|
||||
|
||||
30
citadel.dme
30
citadel.dme
@@ -21,7 +21,6 @@
|
||||
#include "code\__global_init.dm"
|
||||
#include "code\_macros.dm"
|
||||
#include "code\global.dm"
|
||||
#include "code\names.dm"
|
||||
#include "code\world.dm"
|
||||
#include "code\__DEFINES\_bitfields.dm"
|
||||
#include "code\__DEFINES\_cooldowns.dm"
|
||||
@@ -37,7 +36,6 @@
|
||||
#include "code\__DEFINES\admin.dm"
|
||||
#include "code\__DEFINES\appearance.dm"
|
||||
#include "code\__DEFINES\automata.dm"
|
||||
#include "code\__DEFINES\auxtools.dm"
|
||||
#include "code\__DEFINES\belly_modes_vr.dm"
|
||||
#include "code\__DEFINES\callbacks.dm"
|
||||
#include "code\__DEFINES\chat.dm"
|
||||
@@ -90,6 +88,7 @@
|
||||
#include "code\__DEFINES\stat_tracking.dm"
|
||||
#include "code\__DEFINES\status_effects.dm"
|
||||
#include "code\__DEFINES\storage.dm"
|
||||
#include "code\__DEFINES\strack_trace.dm"
|
||||
#include "code\__DEFINES\targeting.dm"
|
||||
#include "code\__DEFINES\text.dm"
|
||||
#include "code\__DEFINES\tgs.config.dm"
|
||||
@@ -217,7 +216,7 @@
|
||||
#include "code\__DEFINES\tools\procs.dm"
|
||||
#include "code\__DEFINES\turfs\change_turf.dm"
|
||||
#include "code\__DEFINES\turfs\turfmakers.dm"
|
||||
#include "code\__HELPERS\_core.dm"
|
||||
#include "code\__HELPERS\_auxtools_api.dm"
|
||||
#include "code\__HELPERS\_global_objects.dm"
|
||||
#include "code\__HELPERS\_lists_tg.dm"
|
||||
#include "code\__HELPERS\_logging.dm"
|
||||
@@ -225,34 +224,41 @@
|
||||
#include "code\__HELPERS\areas.dm"
|
||||
#include "code\__HELPERS\atom_movables.dm"
|
||||
#include "code\__HELPERS\chat.dm"
|
||||
#include "code\__HELPERS\datum.dm"
|
||||
#include "code\__HELPERS\do_after.dm"
|
||||
#include "code\__HELPERS\events.dm"
|
||||
#include "code\__HELPERS\files.dm"
|
||||
#include "code\__HELPERS\filters.dm"
|
||||
#include "code\__HELPERS\game.dm"
|
||||
#include "code\__HELPERS\global_lists.dm"
|
||||
#include "code\__HELPERS\guid.dm"
|
||||
#include "code\__HELPERS\heap.dm"
|
||||
#include "code\__HELPERS\icon_smoothing.dm"
|
||||
#include "code\__HELPERS\icons.dm"
|
||||
#include "code\__HELPERS\mobs.dm"
|
||||
#include "code\__HELPERS\nameof.dm"
|
||||
#include "code\__HELPERS\names.dm"
|
||||
#include "code\__HELPERS\pass.dm"
|
||||
#include "code\__HELPERS\path.dm"
|
||||
#include "code\__HELPERS\piping_colors_lists.dm"
|
||||
#include "code\__HELPERS\ref.dm"
|
||||
#include "code\__HELPERS\roundend.dm"
|
||||
#include "code\__HELPERS\sanitize_values.dm"
|
||||
#include "code\__HELPERS\shell.dm"
|
||||
#include "code\__HELPERS\stack_trace.dm"
|
||||
#include "code\__HELPERS\stoplag.dm"
|
||||
#include "code\__HELPERS\storage.dm"
|
||||
#include "code\__HELPERS\text.dm"
|
||||
#include "code\__HELPERS\time.dm"
|
||||
#include "code\__HELPERS\turfs.dm"
|
||||
#include "code\__HELPERS\type2type.dm"
|
||||
#include "code\__HELPERS\type2type_vr.dm"
|
||||
#include "code\__HELPERS\type_processing.dm"
|
||||
#include "code\__HELPERS\typelists.dm"
|
||||
#include "code\__HELPERS\unsorted.dm"
|
||||
#include "code\__HELPERS\varset_callback.dm"
|
||||
#include "code\__HELPERS\vector.dm"
|
||||
#include "code\__HELPERS\view.dm"
|
||||
#include "code\__HELPERS\icons\flatten.dm"
|
||||
#include "code\__HELPERS\icons\flatten_old.dm"
|
||||
#include "code\__HELPERS\lists\_string_lists.dm"
|
||||
#include "code\__HELPERS\lists\associations.dm"
|
||||
#include "code\__HELPERS\lists\bitflag_lists.dm"
|
||||
@@ -280,6 +286,7 @@
|
||||
#include "code\__HELPERS\sorts\TimSort.dm"
|
||||
#include "code\__HELPERS\text\scramble.dm"
|
||||
#include "code\__HELPERS\type2type\color.dm"
|
||||
#include "code\__HELPERS\type2type\type2type.dm"
|
||||
#include "code\__HELPERS\unsorted\contents.dm"
|
||||
#include "code\__HELPERS\vfx\appearance_cloning.dm"
|
||||
#include "code\__HELPERS\vfx\color_animations.dm"
|
||||
@@ -304,6 +311,7 @@
|
||||
#include "code\_globalvars\lists\mapping.dm"
|
||||
#include "code\_globalvars\lists\medical.dm"
|
||||
#include "code\_globalvars\lists\misc.dm"
|
||||
#include "code\_globalvars\lists\names.dm"
|
||||
#include "code\_globalvars\lists\objects.dm"
|
||||
#include "code\_globalvars\lists\species.dm"
|
||||
#include "code\_js\byjax.dm"
|
||||
@@ -458,6 +466,7 @@
|
||||
#include "code\controllers\subsystem\sound\_sound.dm"
|
||||
#include "code\controllers\subsystem\sound\channel_manager.dm"
|
||||
#include "code\controllers\subsystem\sound\soundbyte_manager.dm"
|
||||
#include "code\datums\_datum.dm"
|
||||
#include "code\datums\ai_law_sets.dm"
|
||||
#include "code\datums\ai_laws.dm"
|
||||
#include "code\datums\appearance.dm"
|
||||
@@ -466,7 +475,6 @@
|
||||
#include "code\datums\category.dm"
|
||||
#include "code\datums\computerfiles.dm"
|
||||
#include "code\datums\datacore.dm"
|
||||
#include "code\datums\datum.dm"
|
||||
#include "code\datums\datumvars.dm"
|
||||
#include "code\datums\EPv2.dm"
|
||||
#include "code\datums\ghost_query.dm"
|
||||
@@ -474,6 +482,7 @@
|
||||
#include "code\datums\mind.dm"
|
||||
#include "code\datums\mixed.dm"
|
||||
#include "code\datums\mutable_appearance.dm"
|
||||
#include "code\datums\periodic_news.dm"
|
||||
#include "code\datums\perspective.dm"
|
||||
#include "code\datums\position_point_vector.dm"
|
||||
#include "code\datums\profile.dm"
|
||||
@@ -644,15 +653,14 @@
|
||||
#include "code\datums\wires\tesla_coil.dm"
|
||||
#include "code\datums\wires\vending.dm"
|
||||
#include "code\datums\wires\wires.dm"
|
||||
#include "code\defines\periodic_news.dm"
|
||||
#include "code\defines\procs\AStar.dm"
|
||||
#include "code\defines\procs\radio.dm"
|
||||
#include "code\defines\procs\sd_Alert.dm"
|
||||
#include "code\defines\procs\statistics.dm"
|
||||
#include "code\game\AStar.dm"
|
||||
#include "code\game\radio.dm"
|
||||
#include "code\game\response_team.dm"
|
||||
#include "code\game\sd_Alert.dm"
|
||||
#include "code\game\shuttle_engines.dm"
|
||||
#include "code\game\skincmd.dm"
|
||||
#include "code\game\sound.dm"
|
||||
#include "code\game\statistics.dm"
|
||||
#include "code\game\trader_visit.dm"
|
||||
#include "code\game\world.dm"
|
||||
#include "code\game\antagonist\_antagonist_setup.dm"
|
||||
|
||||
@@ -36,5 +36,3 @@
|
||||
#define TICK_CHECK_HIGH_PRIORITY ( TICK_USAGE > 95 )
|
||||
/// runs stoplag if tick_usage is above 95, for high priority usage
|
||||
#define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 )
|
||||
|
||||
#define UNTIL(X) while(!(X)) stoplag()
|
||||
|
||||
@@ -54,8 +54,6 @@
|
||||
// Least Common Multiple
|
||||
#define LCM(a, b) (abs(a) / GCD(a, b) * abs(b))
|
||||
|
||||
#define IS_CARDINAL(x) ((x & (x - 1)) == 0)
|
||||
|
||||
#define INVERSE(x) ( 1/(x) )
|
||||
|
||||
// Used for calculating the radioactive strength falloff
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
#define VAR_PROTECTED var
|
||||
#endif
|
||||
|
||||
/proc/auxtools_stack_trace(msg)
|
||||
CRASH(msg)
|
||||
|
||||
/proc/enable_debugging()
|
||||
CRASH("Auxtools not found")
|
||||
|
||||
|
||||
2
code/__DEFINES/strack_trace.dm
Normal file
2
code/__DEFINES/strack_trace.dm
Normal file
@@ -0,0 +1,2 @@
|
||||
/// Gives us the stack trace from CRASH() without ending the current proc.
|
||||
#define stack_trace(message) _stack_trace(message, __FILE__, __LINE__)
|
||||
@@ -24,7 +24,7 @@
|
||||
/*
|
||||
// JSON text files found in the tgstation/strings folder
|
||||
/// File location for brain damage traumas
|
||||
#define BRAIN_DAMAGE_FILE "traumas.json"
|
||||
#define BRAIN_DAMAGE_FILE "pick/traumas.json"
|
||||
/// File location for AI ion laws
|
||||
#define ION_FILE "ion_laws.json"
|
||||
/// File location for pirate names
|
||||
@@ -36,7 +36,7 @@
|
||||
#define ARCADE_FILE "arcade.json"
|
||||
/*
|
||||
/// File location for boomer meme catchphrases
|
||||
#define BOOMER_FILE "boomer.json"
|
||||
#define BOOMER_FILE "pick/boomer.json"
|
||||
/// File location for locations on the station
|
||||
#define LOCATIONS_FILE "locations.json"
|
||||
/// File location for wanted posters messages
|
||||
|
||||
@@ -20,5 +20,34 @@
|
||||
#define SMOOTH_BLACKLIST 3
|
||||
/// Use a whitelist and a blacklist at the same time. atom smoothing only
|
||||
#define SMOOTH_GREYLIST 4
|
||||
#define isCardinal(x) (x == NORTH || x == SOUTH || x == EAST || x == WEST)
|
||||
#define isDiagonal(x) (x == NORTHEAST || x == SOUTHEAST || x == NORTHWEST || x == SOUTHWEST)
|
||||
|
||||
// #define IS_CARDINAL(x) ((x & (x - 1)) == 0) // Cardinal using math.
|
||||
#define IS_CARDINAL(DIR) (DIR == NORTH || DIR == SOUTH || DIR == EAST || DIR == WEST)
|
||||
#define IS_DIAGONAL(DIR) (DIR == NORTHEAST || DIR == SOUTHEAST || DIR == NORTHWEST || DIR == SOUTHWEST)
|
||||
|
||||
|
||||
// Supposedly the fastest way to do this according to https://gist.github.com/Giacom/be635398926bb463b42a
|
||||
|
||||
/**
|
||||
*! Return a list of turfs in a square.
|
||||
*/
|
||||
#define RANGE_TURFS(RADIUS, CENTER) \
|
||||
RECT_TURFS(RADIUS, RADIUS, CENTER)
|
||||
|
||||
#define RECT_TURFS(H_RADIUS, V_RADIUS, CENTER) \
|
||||
block( \
|
||||
locate(max(CENTER.x-(H_RADIUS),1), max(CENTER.y-(V_RADIUS),1), CENTER.z), \
|
||||
locate(min(CENTER.x+(H_RADIUS),world.maxx), min(CENTER.y+(V_RADIUS),world.maxy), CENTER.z), \
|
||||
)
|
||||
|
||||
/**
|
||||
*! Return a list of turfs in a square or null.
|
||||
*/
|
||||
#define RANGE_TURFS_OR_EMPTY(RADIUS, CENTER) \
|
||||
RECT_TURFS_OR_EMPTY(RADIUS, RADIUS, CENTER)
|
||||
|
||||
#define RECT_TURFS_OR_EMPTY(H_RADIUS, V_RADIUS, CENTER) \
|
||||
(CENTER? block( \
|
||||
locate(max(CENTER.x-(H_RADIUS),1), max(CENTER.y-(V_RADIUS),1), CENTER.z), \
|
||||
locate(min(CENTER.x+(H_RADIUS),world.maxx), min(CENTER.y+(V_RADIUS),world.maxy), CENTER.z), \
|
||||
) : list())
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
/proc/auxtools_stack_trace(msg)
|
||||
CRASH(msg)
|
||||
|
||||
GLOBAL_LIST_EMPTY(auxtools_initialized)
|
||||
|
||||
#define AUXTOOLS_CHECK(LIB)\
|
||||
@@ -1,99 +0,0 @@
|
||||
//datum may be null, but it does need to be a typed var
|
||||
#define NAMEOF(datum, X) (#X || ##datum.##X)
|
||||
|
||||
#define VARSET_LIST_CALLBACK(target, var_name, var_value) CALLBACK(GLOBAL_PROC, /proc/___callbackvarset, ##target, ##var_name, ##var_value)
|
||||
//dupe code because dm can't handle 3 level deep macros
|
||||
#define VARSET_CALLBACK(datum, var, var_value) CALLBACK(GLOBAL_PROC, /proc/___callbackvarset, ##datum, NAMEOF(##datum, ##var), ##var_value)
|
||||
//we'll see about those 3-level deep macros
|
||||
#define VARSET_IN(datum, var, var_value, time) addtimer(VARSET_CALLBACK(datum, var, var_value), time)
|
||||
|
||||
/proc/___callbackvarset(list_or_datum, var_name, var_value)
|
||||
if(length(list_or_datum))
|
||||
list_or_datum[var_name] = var_value
|
||||
return
|
||||
var/datum/D = list_or_datum
|
||||
D.vars[var_name] = var_value
|
||||
|
||||
/proc/IsValidSrc(datum/D)
|
||||
if(istype(D))
|
||||
return !QDELETED(D)
|
||||
return FALSE
|
||||
|
||||
//gives us the stack trace from CRASH() without ending the current proc.
|
||||
/proc/stack_trace(msg)
|
||||
CRASH(msg)
|
||||
|
||||
/datum/proc/stack_trace(msg)
|
||||
CRASH(msg)
|
||||
|
||||
GLOBAL_REAL_VAR(list/stack_trace_storage)
|
||||
/proc/gib_stack_trace()
|
||||
stack_trace_storage = list()
|
||||
stack_trace()
|
||||
stack_trace_storage.Cut(1, min(3,stack_trace_storage.len))
|
||||
. = stack_trace_storage
|
||||
stack_trace_storage = null
|
||||
|
||||
//Key thing that stops lag. Cornerstone of performance in ss13, Just sitting here, in unsorted.dm.
|
||||
|
||||
//Increases delay as the server gets more overloaded,
|
||||
//as sleeps aren't cheap and sleeping only to wake up and sleep again is wasteful
|
||||
#define DELTA_CALC max(((max(TICK_USAGE, world.cpu) / 100) * max(Master.sleep_delta-1,1)), 1)
|
||||
|
||||
//returns the number of ticks slept
|
||||
/proc/stoplag(initial_delay)
|
||||
if (!Master || !(Master.current_runlevel & RUNLEVELS_DEFAULT))
|
||||
sleep(world.tick_lag)
|
||||
return 1
|
||||
if (!initial_delay)
|
||||
initial_delay = world.tick_lag
|
||||
. = 0
|
||||
var/i = DS2TICKS(initial_delay)
|
||||
do
|
||||
. += CEILING(i*DELTA_CALC, 1)
|
||||
sleep(i*world.tick_lag*DELTA_CALC)
|
||||
i *= 2
|
||||
while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit))
|
||||
|
||||
#undef DELTA_CALC
|
||||
|
||||
//There's a good reason we have this. I think.
|
||||
/proc/pass()
|
||||
return
|
||||
|
||||
//returns a GUID like identifier (using a mostly made up record format)
|
||||
//guids are not on their own suitable for access or security tokens, as most of their bits are predictable.
|
||||
// (But may make a nice salt to one)
|
||||
/proc/GUID()
|
||||
var/const/GUID_VERSION = "b"
|
||||
var/const/GUID_VARIANT = "d"
|
||||
var/node_id = copytext(md5("[rand()*rand(1,9999999)][world.name][world.hub][world.hub_password][world.internet_address][world.address][world.contents.len][world.status][world.port][rand()*rand(1,9999999)]"), 1, 13)
|
||||
|
||||
var/time_high = "[num2hex(text2num(time2text(world.realtime,"YYYY")), 2)][num2hex(world.realtime, 6)]"
|
||||
|
||||
var/time_mid = num2hex(world.timeofday, 4)
|
||||
|
||||
var/time_low = num2hex(world.time, 3)
|
||||
|
||||
var/time_clock = num2hex(TICK_DELTA_TO_MS(world.tick_usage), 3)
|
||||
|
||||
return "{[time_high]-[time_mid]-[GUID_VERSION][time_low]-[GUID_VARIANT][time_clock]-[node_id]}"
|
||||
/**
|
||||
* \ref behaviour got changed in 512 so this is necesary to replicate old behaviour.
|
||||
* If it ever becomes necesary to get a more performant REF(), this lies here in wait
|
||||
* #define REF(thing) (thing && istype(thing, /datum) && (thing:datum_flags & DF_USE_TAG) && thing:tag ? "[thing:tag]" : "\ref[thing]")
|
||||
**/
|
||||
/proc/REF(input)
|
||||
if(istype(input, /datum))
|
||||
var/datum/thing = input
|
||||
if(thing.datum_flags & DF_USE_TAG)
|
||||
if(!thing.tag)
|
||||
stack_trace("A ref was requested of an object with DF_USE_TAG set but no tag: [thing]")
|
||||
thing.datum_flags &= ~DF_USE_TAG
|
||||
else
|
||||
return "\[[url_encode(thing.tag)]\]"
|
||||
return "\ref[input]"
|
||||
|
||||
/proc/CallAsync(datum/source, proctype, list/arguments)
|
||||
set waitfor = FALSE
|
||||
return call(source, proctype)(arglist(arguments))
|
||||
@@ -62,20 +62,20 @@ This actually tests if they have the same entries and values.
|
||||
|
||||
//for sorting clients or mobs by ckey
|
||||
/proc/sortKey(list/L, order=1)
|
||||
return sortTim(L, order >= 0 ? /proc/cmp_ckey_asc : /proc/cmp_ckey_dsc)
|
||||
return tim_sort(L, order >= 0 ? /proc/cmp_ckey_asc : /proc/cmp_ckey_dsc)
|
||||
|
||||
//Specifically for record datums in a list.
|
||||
/proc/sortRecord(list/L, field = "name", order = 1)
|
||||
GLOB.cmp_field = field
|
||||
return sortTim(L, order >= 0 ? /proc/cmp_records_asc : /proc/cmp_records_dsc)
|
||||
return tim_sort(L, order >= 0 ? /proc/cmp_records_asc : /proc/cmp_records_dsc)
|
||||
|
||||
//any value in a list
|
||||
/proc/sortList(list/L, cmp=/proc/cmp_text_asc)
|
||||
return sortTim(L.Copy(), cmp)
|
||||
return tim_sort(L.Copy(), cmp)
|
||||
|
||||
//uses sortList() but uses the var's name specifically. This should probably be using mergeAtom() instead
|
||||
/proc/sortNames(list/L, order=1)
|
||||
return sortTim(L, order >= 0 ? /proc/cmp_name_asc : /proc/cmp_name_dsc)
|
||||
return tim_sort(L, order >= 0 ? /proc/cmp_name_asc : /proc/cmp_name_dsc)
|
||||
|
||||
|
||||
|
||||
@@ -86,13 +86,15 @@ This actually tests if they have the same entries and values.
|
||||
i++
|
||||
return i
|
||||
|
||||
//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)
|
||||
/**
|
||||
* 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/move_element(list/L, fromIndex, toIndex)
|
||||
if(fromIndex == toIndex || fromIndex+1 == toIndex) //no need to move
|
||||
return
|
||||
if(fromIndex > toIndex)
|
||||
@@ -102,11 +104,12 @@ This actually tests if they have the same entries and values.
|
||||
L.Swap(fromIndex, toIndex)
|
||||
L.Cut(fromIndex, fromIndex+1)
|
||||
|
||||
|
||||
//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)
|
||||
/**
|
||||
* Move elements [fromIndex,fromIndex+len) to [toIndex-len, toIndex)
|
||||
* Same as move_element but for ranges of elements
|
||||
* This will preserve associations ~Carnie
|
||||
*/
|
||||
/proc/move_range(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. The result being, our range we are moving is shifted left or right by dist elements
|
||||
if(fromIndex <= toIndex)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//wrapper macros for easier grepping
|
||||
// Wrapper macros for easier grepping
|
||||
#define DIRECT_OUTPUT(A, B) A << B
|
||||
#define DIRECT_INPUT(A, B) A >> B
|
||||
#define SEND_IMAGE(target, image) DIRECT_OUTPUT(target, image)
|
||||
@@ -11,28 +11,28 @@
|
||||
#define WRITE_LOG(log, text) extools_log_write(log,text,TRUE)
|
||||
#define WRITE_LOG_NO_FORMAT(log, text) extools_log_write(log,text,FALSE)
|
||||
#else
|
||||
//This is an external call, "true" and "false" are how rust parses out booleans
|
||||
// This is an external call, "true" and "false" are how rust parses out booleans
|
||||
#define WRITE_LOG(log, text) rustg_log_write(log, text, "true")
|
||||
#define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false")
|
||||
#endif
|
||||
//print a warning message to world.log
|
||||
// Print a warning message to world.log
|
||||
#define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [UNLINT(src)] usr: [usr].")
|
||||
/proc/warning(msg)
|
||||
msg = "## WARNING: [msg]"
|
||||
log_world(msg)
|
||||
|
||||
//not an error or a warning, but worth to mention on the world log, just in case.
|
||||
// Not an error or a warning, but worth to mention on the world log, just in case.
|
||||
#define NOTICE(MSG) notice(MSG)
|
||||
/proc/notice(msg)
|
||||
msg = "## NOTICE: [msg]"
|
||||
log_world(msg)
|
||||
|
||||
//print a testing-mode debug message to world.log and world
|
||||
// Print a testing-mode debug message to world.log and world
|
||||
#ifdef TESTING
|
||||
#define testing(msg) log_world("## TESTING: [msg]"); to_chat(world, "## TESTING: [msg]")
|
||||
|
||||
GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
|
||||
// we don't really check if a word or name is used twice, be aware of that
|
||||
// We don't really check if a word or name is used twice, be aware of that
|
||||
#define testing_profile_start(NAME, LIST) LIST[NAME] = world.timeofday
|
||||
#define testing_profile_current(NAME, LIST) round((world.timeofday - LIST[NAME])/10,0.1)
|
||||
#define testing_profile_output(NAME, LIST) testing("[LIST["_PROFILE_NAME"]] profile of [NAME] is [testing_profile_current(NAME,LIST)]s")
|
||||
@@ -75,7 +75,9 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
|
||||
#define log_reftracker(msg)
|
||||
#endif
|
||||
|
||||
/* Items with ADMINPRIVATE prefixed are stripped from public logs. */
|
||||
/**
|
||||
* Items with ADMINPRIVATE prefixed are stripped from public logs.
|
||||
*/
|
||||
/proc/log_admin(text)
|
||||
admin_log.Add(text)
|
||||
if (config_legacy.log_admin)
|
||||
@@ -106,7 +108,9 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
|
||||
if (config_legacy.log_admin)
|
||||
WRITE_LOG(GLOB.world_game_log, "ADMINPM: [key_name(source)]->[key_name(dest)]: [html_decode(text)]")
|
||||
|
||||
/* All other items are public. */
|
||||
/**
|
||||
* All other items are public.
|
||||
*/
|
||||
/proc/log_game(text)
|
||||
if (config_legacy.log_game)
|
||||
WRITE_LOG(GLOB.world_game_log, "GAME: [text]")
|
||||
@@ -206,18 +210,24 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
|
||||
/proc/log_subsystem(subsystem, text)
|
||||
WRITE_LOG(GLOB.subsystem_log, "[subsystem]: [text]")
|
||||
|
||||
/* Log to both DD and the logfile. */
|
||||
/**
|
||||
* Log to both DD and the logfile.
|
||||
*/
|
||||
/proc/log_world(text)
|
||||
#ifdef USE_CUSTOM_ERROR_HANDLER
|
||||
WRITE_LOG(GLOB.world_runtime_log, text)
|
||||
#endif
|
||||
SEND_TEXT(world.log, text)
|
||||
|
||||
/* Log to the logfile only. */
|
||||
/**
|
||||
* Log to the logfile only.
|
||||
*/
|
||||
/proc/log_runtime(text)
|
||||
WRITE_LOG(GLOB.world_runtime_log, text)
|
||||
|
||||
/* Rarely gets called; just here in case the config breaks. */
|
||||
/**
|
||||
* Rarely gets called; just here in case the config breaks.
|
||||
*/
|
||||
/proc/log_config(text)
|
||||
WRITE_LOG(GLOB.config_error_log, text)
|
||||
SEND_TEXT(world.log, text)
|
||||
@@ -225,12 +235,15 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
|
||||
/proc/log_mapping(text)
|
||||
WRITE_LOG(GLOB.world_map_error_log, text)
|
||||
|
||||
/* For logging round startup. */
|
||||
/**
|
||||
* For logging round startup.
|
||||
*/
|
||||
/proc/start_log(log)
|
||||
WRITE_LOG(log, "Starting up round ID [GLOB.round_id].\n-------------------------")
|
||||
|
||||
/**
|
||||
* Appends a tgui-related log entry. All arguments are optional.
|
||||
* Appends a tgui-related log entry.
|
||||
* All arguments are optional.
|
||||
*/
|
||||
/proc/log_tgui(user, message, context,
|
||||
datum/tgui_window/window,
|
||||
@@ -261,7 +274,10 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
|
||||
entry += "\n[message]"
|
||||
WRITE_LOG(GLOB.tgui_log, entry)
|
||||
|
||||
/* Close open log handles. This should be called as late as possible, and no logging should hapen after. */
|
||||
/**
|
||||
* Close open log handles.
|
||||
* This should be called as late as possible, and no logging should hapen after.
|
||||
*/
|
||||
/proc/shutdown_logging()
|
||||
#ifdef EXTOOLS_LOGGING
|
||||
extools_finalize_logging()
|
||||
@@ -269,7 +285,9 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
|
||||
rustg_log_close_all()
|
||||
#endif
|
||||
|
||||
/* Helper procs for building detailed log lines */
|
||||
/**
|
||||
* Helper procs for building detailed log lines
|
||||
*/
|
||||
/proc/key_name(whom, include_link = null, include_name = TRUE, highlight_special_characters = TRUE)
|
||||
var/mob/M
|
||||
var/client/C
|
||||
@@ -434,20 +452,30 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
|
||||
|
||||
/// VSTATION LOGGING HELPERS ///
|
||||
|
||||
//pretty print a direction bitflag, can be useful for debugging.
|
||||
/proc/print_dir(var/dir)
|
||||
/**
|
||||
* Pretty print a direction bitflag, can be useful for debugging.
|
||||
*/
|
||||
/proc/print_dir(dir)
|
||||
var/list/comps = list()
|
||||
if(dir & NORTH) comps += "NORTH"
|
||||
if(dir & SOUTH) comps += "SOUTH"
|
||||
if(dir & EAST) comps += "EAST"
|
||||
if(dir & WEST) comps += "WEST"
|
||||
if(dir & UP) comps += "UP"
|
||||
if(dir & DOWN) comps += "DOWN"
|
||||
if(dir & NORTH)
|
||||
comps += "NORTH"
|
||||
if(dir & SOUTH)
|
||||
comps += "SOUTH"
|
||||
if(dir & EAST)
|
||||
comps += "EAST"
|
||||
if(dir & WEST)
|
||||
comps += "WEST"
|
||||
if(dir & UP)
|
||||
comps += "UP"
|
||||
if(dir & DOWN)
|
||||
comps += "DOWN"
|
||||
|
||||
return english_list(comps, nothing_text="0", and_text="|", comma_text="|")
|
||||
|
||||
|
||||
// Helper procs for building detailed log lines
|
||||
/**
|
||||
* Helper procs for building detailed log lines.
|
||||
*/
|
||||
/datum/proc/log_info_line()
|
||||
return "[src] ([type])"
|
||||
|
||||
@@ -463,12 +491,12 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
|
||||
/mob/log_info_line()
|
||||
return "[..()] ([ckey])"
|
||||
|
||||
/proc/log_info_line(var/datum/d)
|
||||
if(!d)
|
||||
/proc/log_info_line(datum/datum)
|
||||
if(!datum)
|
||||
return "*null*"
|
||||
if(!istype(d))
|
||||
return json_encode(d)
|
||||
return d.log_info_line()
|
||||
if(!istype(datum))
|
||||
return json_encode(datum)
|
||||
return datum.log_info_line()
|
||||
|
||||
/mob/proc/simple_info_line()
|
||||
return "[key_name(src)] ([AREACOORD(src)])"
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
for(var/area/A in world)
|
||||
GLOB.sortedAreas.Add(A)
|
||||
|
||||
sortTim(GLOB.sortedAreas, /proc/cmp_name_asc)
|
||||
tim_sort(GLOB.sortedAreas, /proc/cmp_name_asc)
|
||||
setupTeleportLocs() // shitcode patch to make vorecode work until we get rid of this shit meme or refactor it entirely
|
||||
|
||||
/area/proc/addSorted()
|
||||
GLOB.sortedAreas.Add(src)
|
||||
sortTim(GLOB.sortedAreas, /proc/cmp_name_asc)
|
||||
tim_sort(GLOB.sortedAreas, /proc/cmp_name_asc)
|
||||
|
||||
//Takes: Area type as a text string from a variable.
|
||||
//Returns: Instance for the area in the world.
|
||||
|
||||
@@ -18,38 +18,42 @@
|
||||
if(!istype(AM))
|
||||
return
|
||||
|
||||
//Find AM's matrix so we can use it's X/Y pixel shifts
|
||||
//! Find AM's matrix so we can use it's X/Y pixel shifts.
|
||||
var/matrix/M = matrix(AM.transform)
|
||||
|
||||
var/pixel_x_offset = AM.pixel_x + M.get_x_shift()
|
||||
var/pixel_y_offset = AM.pixel_y + M.get_y_shift()
|
||||
|
||||
//Irregular objects
|
||||
//! Irregular objects.
|
||||
if(AM.bound_height != world.icon_size || AM.bound_width != world.icon_size)
|
||||
var/icon/AMicon = icon(AM.icon, AM.icon_state)
|
||||
pixel_x_offset += ((AMicon.Width()/world.icon_size)-1)*(world.icon_size*0.5)
|
||||
pixel_y_offset += ((AMicon.Height()/world.icon_size)-1)*(world.icon_size*0.5)
|
||||
qdel(AMicon)
|
||||
|
||||
//DY and DX
|
||||
//! DY and DX.
|
||||
var/rough_x = round(round(pixel_x_offset,world.icon_size)/world.icon_size)
|
||||
var/rough_y = round(round(pixel_y_offset,world.icon_size)/world.icon_size)
|
||||
|
||||
//Find coordinates
|
||||
var/turf/T = get_turf(AM) //use AM's turfs, as it's coords are the same as AM's AND AM's coords are lost if it is inside another atom
|
||||
//! Find coordinates.
|
||||
//u Use AM's turfs, as it's coords are the same as AM's AND AM's coords are lost if it is inside another atom.
|
||||
var/turf/T = get_turf(AM)
|
||||
var/final_x = T.x + rough_x
|
||||
var/final_y = T.y + rough_y
|
||||
|
||||
if(final_x || final_y)
|
||||
return locate(final_x, final_y, T.z)
|
||||
|
||||
// Walks up the loc tree until it finds a holder of the given holder_type
|
||||
/**
|
||||
* Walks up the loc tree until it finds a holder of the given holder_type.
|
||||
*/
|
||||
/proc/get_holder_of_type(atom/A, holder_type)
|
||||
if(!istype(A)) return
|
||||
if(!istype(A))
|
||||
return
|
||||
for(A, A && !istype(A, holder_type), A=A.loc);
|
||||
return A
|
||||
|
||||
/atom/movable/proc/throw_at_random(var/include_own_turf, var/maxrange, var/speed)
|
||||
/atom/movable/proc/throw_at_random(include_own_turf, maxrange, speed)
|
||||
var/list/turfs = trange(maxrange, src)
|
||||
if(!maxrange)
|
||||
maxrange = 1
|
||||
@@ -58,7 +62,9 @@
|
||||
turfs -= get_turf(src)
|
||||
src.throw_at_old(pick(turfs), maxrange, speed, src)
|
||||
|
||||
//same as do_mob except for movables and it allows both to drift and doesn't draw progressbar
|
||||
/**
|
||||
* Same as do_mob except for movables and it allows both to drift and doesn't draw progressbar.
|
||||
*/
|
||||
/proc/do_atom(atom/movable/user , atom/movable/target, time = 30, uninterruptible = 0,datum/callback/extra_checks = null)
|
||||
if(!user || !target)
|
||||
return TRUE
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
/*
|
||||
|
||||
Here's how to use the chat system with configs
|
||||
|
||||
send2adminchat is a simple function that broadcasts to admin channels
|
||||
|
||||
send2chat is a bit verbose but can be very specific
|
||||
|
||||
The second parameter is a string, this string should be read from a config.
|
||||
What this does is dictacte which TGS4 channels can be sent to.
|
||||
|
||||
For example if you have the following channels in tgs4 set up
|
||||
- Channel 1, Tag: asdf
|
||||
- Channel 2, Tag: bombay,asdf
|
||||
- Channel 3, Tag: Hello my name is asdf
|
||||
- Channel 4, No Tag
|
||||
- Channel 5, Tag: butts
|
||||
|
||||
and you make the call:
|
||||
|
||||
send2chat("I sniff butts", CONFIG_GET(string/where_to_send_sniff_butts))
|
||||
|
||||
and the config option is set like:
|
||||
|
||||
WHERE_TO_SEND_SNIFF_BUTTS asdf
|
||||
|
||||
It will be sent to channels 1 and 2
|
||||
|
||||
Alternatively if you set the config option to just:
|
||||
|
||||
WHERE_TO_SEND_SNIFF_BUTTS
|
||||
|
||||
it will be sent to all connected chats.
|
||||
|
||||
In TGS3 it will always be sent to all connected designated game chats.
|
||||
/**
|
||||
*
|
||||
* Here's how to use the chat system with configs
|
||||
*
|
||||
*? send2adminchat is a simple function that broadcasts to admin channels
|
||||
*
|
||||
*? send2chat is a bit verbose but can be very specific
|
||||
*
|
||||
* The second parameter is a string, this string should be read from a config.
|
||||
* What this does is dictacte which TGS4 channels can be sent to.
|
||||
*
|
||||
* For example if you have the following channels in tgs4 set up
|
||||
* - Channel 1, Tag: asdf
|
||||
* - Channel 2, Tag: bombay,asdf
|
||||
* - Channel 3, Tag: Hello my name is asdf
|
||||
* - Channel 4, No Tag
|
||||
* - Channel 5, Tag: butts
|
||||
*
|
||||
* and you make the call:
|
||||
*
|
||||
* send2chat("I sniff butts", CONFIG_GET(string/where_to_send_sniff_butts))
|
||||
*
|
||||
* and the config option is set like:
|
||||
*
|
||||
* WHERE_TO_SEND_SNIFF_BUTTS asdf
|
||||
*
|
||||
* It will be sent to channels 1 and 2
|
||||
*
|
||||
* Alternatively if you set the config option to just:
|
||||
*
|
||||
* WHERE_TO_SEND_SNIFF_BUTTS
|
||||
*
|
||||
* it will be sent to all connected chats.
|
||||
*
|
||||
* In TGS3 it will always be sent to all connected designated game chats.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
14
code/__HELPERS/datum.dm
Normal file
14
code/__HELPERS/datum.dm
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Check if a datum has not been deleted and is a valid source.
|
||||
*/
|
||||
/proc/is_valid_src(datum/target)
|
||||
if(istype(target))
|
||||
return !QDELETED(target)
|
||||
return FALSE
|
||||
|
||||
/**
|
||||
* async proc call. True beauty.
|
||||
*/
|
||||
/proc/call_async(datum/source, proctype, list/arguments)
|
||||
set waitfor = FALSE
|
||||
return call(source, proctype)(arglist(arguments))
|
||||
@@ -1,26 +1,31 @@
|
||||
/proc/get_station_areas(var/list/area/excluded_areas)
|
||||
/proc/get_station_areas(list/area/excluded_areas)
|
||||
var/list/area/grand_list_of_areas = list()
|
||||
// Assemble areas that all exists (See DM reference if you are confused about loop labels)
|
||||
looping_station_areas:
|
||||
for(var/parentpath in global.the_station_areas)
|
||||
// Check its not excluded
|
||||
// Check its not excluded.
|
||||
for(var/excluded_path in excluded_areas)
|
||||
if(ispath(parentpath, excluded_path))
|
||||
continue looping_station_areas
|
||||
// Otherwise add it and all subtypes that exist on the map to our grand list
|
||||
// Otherwise add it and all subtypes that exist on the map to our grand list.
|
||||
for(var/areapath in typesof(parentpath))
|
||||
var/area/A = locate(areapath) // Check if it actually exists
|
||||
// Check if it actually exists.
|
||||
var/area/A = locate(areapath)
|
||||
if(istype(A) && (A.z in GLOB.using_map.player_levels))
|
||||
grand_list_of_areas += A
|
||||
return grand_list_of_areas
|
||||
|
||||
/** Checks if any living humans are in a given area! */
|
||||
/proc/is_area_occupied(var/area/myarea)
|
||||
/**
|
||||
* Checks if any living humans are in a given area!
|
||||
*/
|
||||
/proc/is_area_occupied(area/myarea)
|
||||
// Testing suggests looping over human_mob_list is quicker than looping over area contents
|
||||
for(var/mob/living/carbon/human/H in human_mob_list)
|
||||
if(H.stat >= DEAD) //Conditions for exclusion here, like if disconnected people start blocking it.
|
||||
// Conditions for exclusion here, like if disconnected people start blocking it.
|
||||
if(H.stat >= DEAD)
|
||||
continue
|
||||
var/area/A = get_area(H)
|
||||
if(A == myarea) //The loc of a turf is the area it is in.
|
||||
return 1
|
||||
return 0
|
||||
// The loc of a turf is the area it is in.
|
||||
if(A == myarea)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
@@ -43,13 +43,17 @@ GLOBAL_VAR_INIT(fileaccess_timer, 0)
|
||||
|
||||
return path
|
||||
|
||||
#define FTPDELAY 50 // 50 tick delay to discourage spam
|
||||
#define ADMIN_FTPDELAY_MODIFIER 0.5 //Admins get to spam files faster since we ~trust~ them!
|
||||
/* This proc is a failsafe to prevent spamming of file requests.
|
||||
It is just a timer that only permits a download every [FTPDELAY] ticks.
|
||||
This can be changed by modifying FTPDELAY's value above.
|
||||
|
||||
PLEASE USE RESPONSIBLY, Some log files can reach sizes of 4MB! */
|
||||
// 50 tick delay to discourage spam
|
||||
#define FTPDELAY 50
|
||||
// Admins get to spam files faster since we "trust" them!
|
||||
#define ADMIN_FTPDELAY_MODIFIER 0.5
|
||||
/**
|
||||
* This proc is a failsafe to prevent spamming of file requests.
|
||||
* It is just a timer that only permits a download every [FTPDELAY] ticks.
|
||||
* This can be changed by modifying FTPDELAY's value above.
|
||||
*
|
||||
*! PLEASE USE RESPONSIBLY, Some log files can reach sizes of 4MB!
|
||||
*/
|
||||
/client/proc/file_spam_check()
|
||||
var/time_to_wait = GLOB.fileaccess_timer - world.time
|
||||
if(time_to_wait > 0)
|
||||
@@ -71,7 +75,7 @@ GLOBAL_VAR_INIT(fileaccess_timer, 0)
|
||||
var/current_dir = pop(jobs)
|
||||
var/list/new_filenames = flist(current_dir)
|
||||
for(var/new_filename in new_filenames)
|
||||
// if filename ends in / it is a directory, append to currdir
|
||||
// If filename ends in / it is a directory, append to currdir.
|
||||
if(findtext(new_filename, "/", -1))
|
||||
jobs += current_dir + new_filename
|
||||
else
|
||||
@@ -81,15 +85,19 @@ GLOBAL_VAR_INIT(fileaccess_timer, 0)
|
||||
/proc/pathflatten(path)
|
||||
return replacetext(path, "/", "_")
|
||||
|
||||
/// Returns the md5 of a file at a given path.
|
||||
/**
|
||||
* Returns the md5 of a file at a given path.
|
||||
*/
|
||||
/proc/md5filepath(path)
|
||||
. = md5(file(path))
|
||||
|
||||
/// Save file as an external file then md5 it.
|
||||
/// Used because md5ing files stored in the rsc sometimes gives incorrect md5 results.
|
||||
/**
|
||||
* Save file as an external file then md5 it.
|
||||
* Used because md5ing files stored in the rsc sometimes gives incorrect md5 results.
|
||||
*/
|
||||
/proc/md5asfile(file)
|
||||
var/static/notch = 0
|
||||
// its importaint this code can handle md5filepath sleeping instead of hard blocking, if it's converted to use rust_g.
|
||||
// It's importaint this code can handle md5filepath sleeping instead of hard blocking, if it's converted to use rust_g.
|
||||
var/filename = "tmp/md5asfile.[world.realtime].[world.timeofday].[world.time].[world.tick_usage].[notch]"
|
||||
notch = WRAP(notch+1, 0, 2**15)
|
||||
fcopy(file, filename)
|
||||
@@ -109,9 +117,11 @@ GLOBAL_VAR_INIT(fileaccess_timer, 0)
|
||||
*/
|
||||
/proc/sanitize_filepath(path)
|
||||
. = ""
|
||||
var/delimiter = "/" //Very much intentionally hardcoded
|
||||
// Very much intentionally hardcoded.
|
||||
var/delimiter = "/"
|
||||
var/list/all_nodes = splittext(path, delimiter)
|
||||
for(var/node in all_nodes)
|
||||
if(.)
|
||||
. += delimiter // Add the delimiter before each successive node.
|
||||
// Add the delimiter before each successive node.
|
||||
. += delimiter
|
||||
. += SANITIZE_FILENAME(node)
|
||||
|
||||
@@ -138,8 +138,10 @@ GLOBAL_LIST_INIT(master_filter_info, list(
|
||||
|
||||
#undef ICON_NOT_SET
|
||||
|
||||
//Helpers to generate lists for filter helpers
|
||||
//This is the only practical way of writing these that actually produces sane lists
|
||||
/**
|
||||
* Helpers to generate lists for filter helpers.
|
||||
* This is the only practical way of writing these that actually produces sane lists.
|
||||
*/
|
||||
/proc/alpha_mask_filter(x, y, icon/icon, render_source, flags)
|
||||
. = list("type" = "alpha")
|
||||
if(!isnull(x))
|
||||
@@ -296,7 +298,7 @@ GLOBAL_LIST_INIT(master_filter_info, list(
|
||||
|
||||
/proc/apply_wibbly_filters(atom/in_atom, length)
|
||||
for(var/i in 1 to 7)
|
||||
//This is a very baffling and strange way of doing this but I am just preserving old functionality
|
||||
// This is a very baffling and strange way of doing this but I am just preserving old functionality.
|
||||
var/X
|
||||
var/Y
|
||||
var/rsq
|
||||
@@ -304,7 +306,8 @@ GLOBAL_LIST_INIT(master_filter_info, list(
|
||||
X = 60*rand() - 30
|
||||
Y = 60*rand() - 30
|
||||
rsq = X*X + Y*Y
|
||||
while(rsq<100 || rsq>900) // Yeah let's just loop infinitely due to bad luck what's the worst that could happen?
|
||||
// Yeah let's just loop infinitely due to bad luck what's the worst that could happen?
|
||||
while(rsq<100 || rsq>900)
|
||||
var/random_roll = rand()
|
||||
in_atom.add_filter("wibbly-[i]", 5, wave_filter(x = X, y = Y, size = rand() * 2.5 + 0.5, offset = random_roll))
|
||||
var/filter = in_atom.get_filter("wibbly-[i]")
|
||||
|
||||
@@ -138,7 +138,9 @@
|
||||
//turfs += centerturf
|
||||
return atoms
|
||||
|
||||
/// Alternative to range (ONLY processes turfs and thus less intensive).
|
||||
/**
|
||||
* Alternative to range (ONLY processes turfs and thus less intensive).
|
||||
*/
|
||||
/proc/trange(rad = 0, turf/centre = null)
|
||||
if(!centre)
|
||||
return
|
||||
@@ -168,7 +170,10 @@
|
||||
turfs += T
|
||||
return turfs
|
||||
|
||||
/proc/circleviewturfs(center = usr, radius = 3) // Is there even a diffrence between this proc and circlerangeturfs()?
|
||||
/**
|
||||
* Is there even a diffrence between this proc and circlerangeturfs()?
|
||||
*/
|
||||
/proc/circleviewturfs(center = usr, radius = 3)
|
||||
|
||||
var/turf/centerturf = get_turf(center)
|
||||
var/list/turfs = new/list()
|
||||
@@ -218,7 +223,9 @@
|
||||
|
||||
return L
|
||||
|
||||
/// Returns a list of mobs and/or objects in range of R from source. Used in radio and say code.
|
||||
/**
|
||||
* Returns a list of mobs and/or objects in range of R from source. Used in radio and say code.
|
||||
*/
|
||||
/proc/get_mobs_or_objects_in_view(R, atom/source, include_mobs = 1, include_objects = 1)
|
||||
|
||||
var/turf/T = get_turf(source)
|
||||
@@ -295,14 +302,15 @@
|
||||
var/list/hearturfs = list()
|
||||
|
||||
for(var/thing in hear)
|
||||
if(istype(thing, /obj)) //Can't use isobj() because /atom/movable returns true in that, and so lighting overlays would be included
|
||||
// Can't use isobj() because /atom/movable returns true in that, and so lighting overlays would be included.
|
||||
if(istype(thing, /obj))
|
||||
objs += thing
|
||||
hearturfs |= get_turf(thing)
|
||||
if(ismob(thing))
|
||||
mobs += thing
|
||||
hearturfs |= get_turf(thing)
|
||||
|
||||
//A list of every mob with a client
|
||||
// A list of every mob with a client.
|
||||
for(var/mob in player_list)
|
||||
if(!ismob(mob))
|
||||
player_list -= mob
|
||||
@@ -315,14 +323,16 @@
|
||||
var/mob/M = mob
|
||||
if(M && M.stat == DEAD && remote_ghosts && !M.forbid_seeing_deadchat)
|
||||
switch(type)
|
||||
if(1) //Audio messages use ghost_ears
|
||||
// Audio messages use ghost_ears.
|
||||
if(1)
|
||||
if(M.is_preference_enabled(/datum/client_preference/ghost_ears))
|
||||
mobs |= M
|
||||
if(2) //Visual messages use ghost_sight
|
||||
// Visual messages use ghost_sight.
|
||||
if(2)
|
||||
if(M.is_preference_enabled(/datum/client_preference/ghost_sight))
|
||||
mobs |= M
|
||||
|
||||
//For objects below the top level who still want to hear
|
||||
// For objects below the top level who still want to hear.
|
||||
for(var/obj in listening_objects)
|
||||
if(get_turf(obj) in hearturfs)
|
||||
objs |= obj
|
||||
@@ -368,7 +378,9 @@
|
||||
for(var/client/C in show_to)
|
||||
C.images -= I
|
||||
|
||||
/// Wrapper for flick_overlay, flicks to everyone who can see the target atom.
|
||||
/**
|
||||
* Wrapper for flick_overlay, flicks to everyone who can see the target atom.
|
||||
*/
|
||||
/proc/flick_overlay_view(image/I, atom/target, duration, gc_after)
|
||||
var/list/viewing = list()
|
||||
for(var/m in viewers(target))
|
||||
@@ -390,12 +402,15 @@
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/// Returns the position of a step from start away from finish, in one of the cardinal directions.
|
||||
/**
|
||||
* Returns the position of a step from start away from finish, in one of the cardinal directions.
|
||||
*/
|
||||
/proc/get_cardinal_step_away(atom/start, atom/finish)
|
||||
//returns only NORTH, SOUTH, EAST, or WEST
|
||||
// Returns only NORTH, SOUTH, EAST, or WEST
|
||||
var/dx = finish.x - start.x
|
||||
var/dy = finish.y - start.y
|
||||
if(abs(dy) > abs (dx)) //slope is above 1:1 (move horizontally in a tie)
|
||||
// Slope is above 1:1 (move horizontally in a tie)
|
||||
if(abs(dy) > abs (dx))
|
||||
if(dy > 0)
|
||||
return get_step(start, SOUTH)
|
||||
else
|
||||
@@ -409,29 +424,37 @@
|
||||
/proc/get_mob_by_key(key)
|
||||
return GLOB.directory[ckey(key)]
|
||||
|
||||
// Will return a list of active candidates. It increases the buffer 5 times until it finds a candidate which is active within the buffer.
|
||||
/**
|
||||
* Will return a list of active candidates.
|
||||
* It increases the buffer 5 times until it finds a candidate which is active within the buffer.
|
||||
*/
|
||||
/proc/get_active_candidates(buffer = 1)
|
||||
|
||||
var/list/candidates = list() //List of candidate KEYS to assume control of the new larva ~Carn
|
||||
/// List of candidate KEYS to assume control of the new larva ~Carn
|
||||
var/list/candidates = list()
|
||||
var/i = 0
|
||||
while(candidates.len <= 0 && i < 5)
|
||||
for(var/mob/observer/dead/G in player_list)
|
||||
if(((G.client.inactivity/10)/60) <= buffer + i) // the most active players are more likely to become an alien
|
||||
// The most active players are more likely to become an alien.
|
||||
if(((G.client.inactivity/10)/60) <= buffer + i)
|
||||
if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD))
|
||||
candidates += G.key
|
||||
i++
|
||||
return candidates
|
||||
|
||||
// Same as above but for alien candidates.
|
||||
|
||||
/**
|
||||
* Same as above but for alien candidates.
|
||||
*/
|
||||
/proc/get_alien_candidates()
|
||||
|
||||
var/list/candidates = list() //List of candidate KEYS to assume control of the new larva ~Carn
|
||||
/// List of candidate KEYS to assume control of the new larva ~Carn
|
||||
var/list/candidates = list()
|
||||
var/i = 0
|
||||
while(candidates.len <= 0 && i < 5)
|
||||
for(var/mob/observer/dead/G in player_list)
|
||||
if(G.client.prefs.be_special & BE_ALIEN)
|
||||
if(((G.client.inactivity/10)/60) <= ALIEN_SELECT_AFK_BUFFER + i) // the most active players are more likely to become an alien
|
||||
// The most active players are more likely to become an alien.
|
||||
if(((G.client.inactivity/10)/60) <= ALIEN_SELECT_AFK_BUFFER + i)
|
||||
if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD))
|
||||
candidates += G.key
|
||||
i++
|
||||
@@ -545,18 +568,18 @@
|
||||
mixedcolor += weight[i]*color[i]
|
||||
mixedcolor = round(mixedcolor)
|
||||
|
||||
//until someone writes a formal proof for this algorithm, let's keep this in
|
||||
// if(mixedcolor<0x00 || mixedcolor>0xFF)
|
||||
// return 0
|
||||
//that's not the kind of operation we are running here, nerd
|
||||
// Until someone writes a formal proof for this algorithm, let's keep this in.
|
||||
// if(mixedcolor<0x00 || mixedcolor>0xFF)
|
||||
// return 0
|
||||
// That's not the kind of operation we are running here, NERD.
|
||||
mixedcolor=min(max(mixedcolor,0),255)
|
||||
|
||||
return mixedcolor
|
||||
|
||||
/**
|
||||
* Gets the highest and lowest pressures from the tiles in cardinal directions
|
||||
* around us, then checks the difference.
|
||||
*/
|
||||
* Gets the highest and lowest pressures from the tiles in cardinal directions
|
||||
* around us, then checks the difference.
|
||||
*/
|
||||
/proc/getOPressureDifferential(turf/loc)
|
||||
var/minp=16777216;
|
||||
var/maxp=0;
|
||||
@@ -627,7 +650,9 @@
|
||||
return
|
||||
winset(client_or_usr, "mainwindow", "flash=5")
|
||||
|
||||
/// Used for the multiz camera console stolen from vorestation.
|
||||
/**
|
||||
* Used for the multiz camera console stolen from virgo.
|
||||
*/
|
||||
/proc/get_bbox_of_atoms(list/atoms)
|
||||
var/list/list_x = list()
|
||||
var/list/list_y = list()
|
||||
@@ -643,7 +668,7 @@
|
||||
|
||||
/proc/recursive_mob_check(atom/O, list/L = list(), recursion_limit = 3, client_check = 1, sight_check = 1, include_radio = 1)
|
||||
|
||||
//GLOB.debug_mob += O.contents.len
|
||||
// GLOB.debug_mob += O.contents.len
|
||||
if(!recursion_limit)
|
||||
return L
|
||||
for(var/atom/A in O.contents)
|
||||
@@ -656,7 +681,7 @@
|
||||
if(sight_check && !isInSight(A, O))
|
||||
continue
|
||||
L |= M
|
||||
//log_world("[recursion_limit] = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])")
|
||||
// log_world("[recursion_limit] = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])")
|
||||
|
||||
else if(include_radio && istype(A, /obj/item/radio))
|
||||
if(sight_check && !isInSight(A, O))
|
||||
@@ -683,7 +708,7 @@
|
||||
var/mob/M = A
|
||||
if(M.client || include_clientless)
|
||||
hear += M
|
||||
//log_world("Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])")
|
||||
// log_world("Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])")
|
||||
else if(istype(A, /obj/item/radio))
|
||||
hear += A
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ GLOBAL_LIST_EMPTY(mannequins)
|
||||
else
|
||||
hair_styles_male_list += H.name
|
||||
hair_styles_female_list += H.name
|
||||
sortTim(hair_styles_list, /proc/cmp_name_asc, associative = TRUE)
|
||||
tim_sort(hair_styles_list, /proc/cmp_name_asc, associative = TRUE)
|
||||
|
||||
//Facial Hair - Initialise all /datum/sprite_accessory/facial_hair into an list indexed by facialhair-style name
|
||||
paths = typesof(/datum/sprite_accessory/facial_hair) - /datum/sprite_accessory/facial_hair
|
||||
@@ -181,7 +181,7 @@ GLOBAL_LIST_EMPTY(mannequins)
|
||||
else
|
||||
facial_hair_styles_male_list += H.name
|
||||
facial_hair_styles_female_list += H.name
|
||||
sortTim(facial_hair_styles_list, /proc/cmp_name_asc, associative = TRUE)
|
||||
tim_sort(facial_hair_styles_list, /proc/cmp_name_asc, associative = TRUE)
|
||||
|
||||
//Body markings - Initialise all /datum/sprite_accessory/marking into an list indexed by marking name
|
||||
paths = typesof(/datum/sprite_accessory/marking) - /datum/sprite_accessory/marking
|
||||
@@ -196,7 +196,7 @@ GLOBAL_LIST_EMPTY(mannequins)
|
||||
continue
|
||||
|
||||
body_marking_styles_list[M.name] = M
|
||||
sortTim(body_marking_styles_list, /proc/cmp_name_asc, associative = TRUE)
|
||||
tim_sort(body_marking_styles_list, /proc/cmp_name_asc, associative = TRUE)
|
||||
|
||||
//List of job. I can't believe this was calculated multiple times per tick!
|
||||
paths = typesof(/datum/job)-/datum/job
|
||||
|
||||
20
code/__HELPERS/guid.dm
Normal file
20
code/__HELPERS/guid.dm
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Returns a GUID like identifier (using a mostly made up record format)
|
||||
* GUIDs are not on their own suitable for access or security tokens, as most of their bits are predictable.
|
||||
*
|
||||
* (But may make a nice salt to one)
|
||||
*/
|
||||
/proc/GUID()
|
||||
var/const/GUID_VERSION = "b"
|
||||
var/const/GUID_VARIANT = "d"
|
||||
var/node_id = copytext(md5("[rand() * rand(1, 9999999)][world.name][world.hub][world.hub_password][world.internet_address][world.address][world.contents.len][world.status][world.port][rand() * rand(1,9999999)]"), 1, 13)
|
||||
|
||||
var/time_high = "[num2hex(text2num(time2text(world.realtime,"YYYY")), 2)][num2hex(world.realtime, 6)]"
|
||||
|
||||
var/time_mid = num2hex(world.timeofday, 4)
|
||||
|
||||
var/time_low = num2hex(world.time, 3)
|
||||
|
||||
var/time_clock = num2hex(TICK_DELTA_TO_MS(world.tick_usage), 3)
|
||||
|
||||
return "{[time_high]-[time_mid]-[GUID_VERSION][time_low]-[GUID_VARIANT][time_clock]-[node_id]}"
|
||||
@@ -11,22 +11,27 @@
|
||||
cmp = compare
|
||||
|
||||
/datum/heap/Destroy(force, ...)
|
||||
for(var/i in L) // because this is before the list helpers are loaded
|
||||
qdel(i)
|
||||
// Because this is before the list helpers are loaded.
|
||||
for(var/heap in L)
|
||||
qdel(heap)
|
||||
L = null
|
||||
return ..()
|
||||
|
||||
/datum/heap/proc/is_empty()
|
||||
return !length(L)
|
||||
|
||||
//insert and place at its position a new node in the heap
|
||||
/**
|
||||
* Insert and place at its position a new node in the heap.
|
||||
*/
|
||||
/datum/heap/proc/insert(atom/A)
|
||||
|
||||
L.Add(A)
|
||||
swim(length(L))
|
||||
|
||||
//removes and returns the first element of the heap
|
||||
//(i.e the max or the min dependant on the comparison function)
|
||||
/**
|
||||
* Removes and returns the first element of the heap.
|
||||
* (i.e the max or the min dependant on the comparison function)
|
||||
*/
|
||||
/datum/heap/proc/pop()
|
||||
if(!length(L))
|
||||
return 0
|
||||
@@ -38,7 +43,9 @@
|
||||
sink(1)
|
||||
|
||||
|
||||
//Get a node up to its right position in the heap
|
||||
/**
|
||||
* Get a node up to its right position in the heap.
|
||||
*/
|
||||
/datum/heap/proc/swim(index)
|
||||
var/parent = round(index * 0.5)
|
||||
|
||||
@@ -47,7 +54,9 @@
|
||||
index = parent
|
||||
parent = round(index * 0.5)
|
||||
|
||||
//Get a node down to its right position in the heap
|
||||
/**
|
||||
* Get a node down to its right position in the heap.
|
||||
*/
|
||||
/datum/heap/proc/sink(index)
|
||||
var/g_child = get_greater_child(index)
|
||||
|
||||
@@ -56,8 +65,10 @@
|
||||
index = g_child
|
||||
g_child = get_greater_child(index)
|
||||
|
||||
//Returns the greater (relative to the comparison proc) of a node children
|
||||
//or 0 if there's no child
|
||||
/**
|
||||
* Returns the greater (relative to the comparison proc) of a node children
|
||||
* or 0 if there's no child.
|
||||
*/
|
||||
/datum/heap/proc/get_greater_child(index)
|
||||
if(index * 2 > length(L))
|
||||
return 0
|
||||
@@ -70,7 +81,9 @@
|
||||
else
|
||||
return index * 2
|
||||
|
||||
//Replaces a given node so it verify the heap condition
|
||||
/**
|
||||
* Replaces a given node so it verify the heap condition.
|
||||
*/
|
||||
/datum/heap/proc/resort(atom/A)
|
||||
var/index = L.Find(A)
|
||||
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
|
||||
//generic (by snowflake) tile smoothing code; smooth your icons with this!
|
||||
/*
|
||||
Each tile is divided in 4 corners, each corner has an appearance associated to it; the tile is then overlayed by these 4 appearances
|
||||
To use this, just set your atom's 'smoothing_flags' var to 1. If your atom can be moved/unanchored, set its 'can_be_unanchored' var to 1.
|
||||
If you don't want your atom's icon to smooth with anything but atoms of the same type, set the list 'canSmoothWith' to null;
|
||||
Otherwise, put all the smoothing groups you want the atom icon to smooth with in 'canSmoothWith', including the group of the atom itself.
|
||||
Smoothing groups are just shared flags between objects. If one of the 'canSmoothWith' of A matches one of the `smoothing_groups` of B, then A will smooth with B.
|
||||
|
||||
Each atom has its own icon file with all the possible corner states. See 'smooth_wall.dmi' for a template.
|
||||
|
||||
DIAGONAL SMOOTHING INSTRUCTIONS
|
||||
To make your atom smooth diagonally you need all the proper icon states (see 'smooth_wall.dmi' for a template) and
|
||||
to add the 'SMOOTH_DIAGONAL_CORNERS' flag to the atom's smoothing_flags var (in addition to either SMOOTH_TRUE or SMOOTH_MORE).
|
||||
|
||||
For turfs, what appears under the diagonal corners depends on the turf that was in the same position previously: if you make a wall on
|
||||
a plating floor, you will see plating under the diagonal wall corner, if it was space, you will see space.
|
||||
|
||||
If you wish to map a diagonal wall corner with a fixed underlay, you must configure the turf's 'fixed_underlay' list var, like so:
|
||||
fixed_underlay = list("icon"='icon_file.dmi', "icon_state"="iconstatename")
|
||||
A non null 'fixed_underlay' list var will skip copying the previous turf appearance and always use the list. If the list is
|
||||
not set properly, the underlay will default to regular floor plating.
|
||||
|
||||
To see an example of a diagonal wall, see '/turf/closed/wall/mineral/titanium' and its subtypes.
|
||||
*/
|
||||
/**
|
||||
* Each tile is divided in 4 corners, each corner has an appearance associated to it; the tile is then overlayed by these 4 appearances
|
||||
* To use this, just set your atom's 'smoothing_flags' var to 1. If your atom can be moved/unanchored, set its 'can_be_unanchored' var to 1.
|
||||
* If you don't want your atom's icon to smooth with anything but atoms of the same type, set the list 'canSmoothWith' to null;
|
||||
* Otherwise, put all the smoothing groups you want the atom icon to smooth with in 'canSmoothWith', including the group of the atom itself.
|
||||
* Smoothing groups are just shared flags between objects. If one of the 'canSmoothWith' of A matches one of the `smoothing_groups` of B, then A will smooth with B.
|
||||
*
|
||||
* Each atom has its own icon file with all the possible corner states. See 'smooth_wall.dmi' for a template.
|
||||
*
|
||||
* DIAGONAL SMOOTHING INSTRUCTIONS
|
||||
* To make your atom smooth diagonally you need all the proper icon states (see 'smooth_wall.dmi' for a template) and
|
||||
* to add the 'SMOOTH_DIAGONAL_CORNERS' flag to the atom's smoothing_flags var (in addition to either SMOOTH_TRUE or SMOOTH_MORE).
|
||||
*
|
||||
* For turfs, what appears under the diagonal corners depends on the turf that was in the same position previously: if you make a wall on
|
||||
* a plating floor, you will see plating under the diagonal wall corner, if it was space, you will see space.
|
||||
*
|
||||
* If you wish to map a diagonal wall corner with a fixed underlay, you must configure the turf's 'fixed_underlay' list var, like so:
|
||||
* fixed_underlay = list("icon"='icon_file.dmi', "icon_state"="iconstatename")
|
||||
* A non null 'fixed_underlay' list var will skip copying the previous turf appearance and always use the list. If the list is
|
||||
* not set properly, the underlay will default to regular floor plating.
|
||||
*
|
||||
* To see an example of a diagonal wall, see '/turf/closed/wall/mineral/titanium' and its subtypes.
|
||||
*/
|
||||
|
||||
#define SET_ADJ_IN_DIR(source, junction, direction, direction_flag) \
|
||||
do { \
|
||||
@@ -62,8 +62,9 @@
|
||||
}; \
|
||||
} while(FALSE)
|
||||
|
||||
|
||||
///Scans all adjacent turfs to find targets to smooth with.
|
||||
/**
|
||||
* Scans all adjacent turfs to find targets to smooth with.
|
||||
*/
|
||||
/atom/proc/calculate_adjacencies()
|
||||
. = NONE
|
||||
|
||||
@@ -74,9 +75,11 @@
|
||||
switch(find_type_in_direction(direction))
|
||||
if(NULLTURF_BORDER)
|
||||
if((smoothing_flags & SMOOTH_BORDER))
|
||||
. |= direction //BYOND and smooth dirs are the same for cardinals
|
||||
// BYOND and smooth dirs are the same for cardinals.
|
||||
. |= direction
|
||||
if(ADJ_FOUND)
|
||||
. |= direction //BYOND and smooth dirs are the same for cardinals
|
||||
// BYOND and smooth dirs are the same for cardinals.
|
||||
. |= direction
|
||||
|
||||
if(. & NORTH_JUNCTION)
|
||||
if(. & WEST_JUNCTION)
|
||||
@@ -117,7 +120,10 @@
|
||||
return NONE
|
||||
return ..()
|
||||
|
||||
///do not use, use QUEUE_SMOOTH(atom)
|
||||
/**
|
||||
*! DO NOT USE
|
||||
*! USE QUEUE_SMOOTH(atom)
|
||||
*/
|
||||
/atom/proc/smooth_icon()
|
||||
smoothing_flags &= ~SMOOTH_QUEUED
|
||||
flags |= HTML_USE_INITAL_ICON
|
||||
@@ -131,7 +137,7 @@
|
||||
else if(smoothing_flags & SMOOTH_BITMASK)
|
||||
bitmask_smooth()
|
||||
else if(smoothing_flags & SMOOTH_CUSTOM)
|
||||
custom_smooth(calculate_adjacencies()) // citrp snowflake smoothing for turfs
|
||||
custom_smooth(calculate_adjacencies()) //! citrp snowflake smoothing for turfs
|
||||
else
|
||||
CRASH("smooth_icon called for [src] with smoothing_flags == [smoothing_flags]")
|
||||
SEND_SIGNAL(src, COMSIG_ATOM_SMOOTHED_ICON)
|
||||
@@ -257,7 +263,9 @@
|
||||
add_overlay(new_overlays)
|
||||
|
||||
|
||||
///Scans direction to find targets to smooth with.
|
||||
/**
|
||||
* Scans direction to find targets to smooth with.
|
||||
*/
|
||||
/atom/proc/find_type_in_direction(direction)
|
||||
var/turf/target_turf = get_step(src, direction)
|
||||
if(!target_turf)
|
||||
@@ -297,7 +305,6 @@
|
||||
* Basic smoothing proc. The atom checks for adjacent directions to smooth with and changes the icon_state based on that.
|
||||
*
|
||||
* Returns the previous smoothing_junction state so the previous state can be compared with the new one after the proc ends, and see the changes, if any.
|
||||
*
|
||||
*/
|
||||
/atom/proc/bitmask_smooth()
|
||||
var/new_junction = NONE
|
||||
@@ -326,7 +333,9 @@
|
||||
set_smoothed_icon_state(new_junction)
|
||||
|
||||
|
||||
///Changes the icon state based on the new junction bitmask. Returns the old junction value.
|
||||
/**
|
||||
* Changes the icon state based on the new junction bitmask. Returns the old junction value.
|
||||
*/
|
||||
/atom/proc/set_smoothed_icon_state(new_junction)
|
||||
. = smoothing_junction
|
||||
smoothing_junction = new_junction
|
||||
@@ -334,7 +343,8 @@
|
||||
|
||||
/turf/set_smoothed_icon_state(new_junction)
|
||||
. = ..()
|
||||
if(!density) // we only do underlays for walls atm
|
||||
// We only do underlays for walls atm.
|
||||
if(!density)
|
||||
return
|
||||
if(smoothing_flags & SMOOTH_DIAGONAL_CORNERS)
|
||||
switch(new_junction)
|
||||
@@ -346,7 +356,7 @@
|
||||
NORTH_JUNCTION|WEST_JUNCTION|NORTHWEST_JUNCTION,
|
||||
NORTH_JUNCTION|EAST_JUNCTION|NORTHEAST_JUNCTION,
|
||||
SOUTH_JUNCTION|WEST_JUNCTION|SOUTHWEST_JUNCTION,
|
||||
SOUTH_JUNCTION|EAST_JUNCTION|SOUTHEAST_JUNCTION
|
||||
SOUTH_JUNCTION|EAST_JUNCTION|SOUTHEAST_JUNCTION,
|
||||
)
|
||||
icon_state = "[base_icon_state]-[smoothing_junction]-d"
|
||||
if(!fixed_underlay && new_junction != .) // Mutable underlays?
|
||||
@@ -366,7 +376,9 @@
|
||||
underlay_appearance.icon_state = DEFAULT_UNDERLAY_ICON_STATE
|
||||
underlays += underlay_appearance
|
||||
|
||||
//Icon smoothing helpers
|
||||
/**
|
||||
* Icon smoothing helpers.
|
||||
*/
|
||||
/proc/smooth_zlevel(zlevel, now = FALSE)
|
||||
var/list/away_turfs = block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel))
|
||||
for(var/V in away_turfs)
|
||||
@@ -396,7 +408,9 @@
|
||||
cut_overlay(bottom_left_corner)
|
||||
bottom_left_corner = null
|
||||
|
||||
/// Internal: Takes icon states as text to replace smoothing corner overlays
|
||||
/**
|
||||
* Internal: Takes icon states as text to replace smoothing corner overlays.
|
||||
*/
|
||||
/atom/proc/replace_smooth_overlays(nw, ne, sw, se)
|
||||
clear_smooth_overlays()
|
||||
var/mutable_appearance/temp_ma
|
||||
|
||||
@@ -161,20 +161,20 @@ mob
|
||||
// Give it a name for the cache
|
||||
var/iconName = "[ckey(src.name)]_flattened.dmi"
|
||||
// Send the icon to src's local cache
|
||||
src<<browse_rsc(getFlatIcon(src), iconName)
|
||||
src<<browse_rsc(get_flat_icon(src), iconName)
|
||||
// Display the icon in their browser
|
||||
src<<browse("<body bgcolor='#000000'><p><img src='[iconName]'></p></body>")
|
||||
|
||||
Output_Icon()
|
||||
set name = "2. Output Icon"
|
||||
to_chat(src, "Icon is: [icon2base64html(getFlatIcon(src))]")
|
||||
to_chat(src, "Icon is: [icon2base64html(get_flat_icon(src))]")
|
||||
|
||||
Label_Icon()
|
||||
set name = "3. Label Icon"
|
||||
// Give it a name for the cache
|
||||
var/iconName = "[ckey(src.name)]_flattened.dmi"
|
||||
// Copy the file to the rsc manually
|
||||
var/icon/I = fcopy_rsc(getFlatIcon(src))
|
||||
var/icon/I = fcopy_rsc(get_flat_icon(src))
|
||||
// Send the icon to src's local cache
|
||||
src<<browse_rsc(I, iconName)
|
||||
// Update the label to show it
|
||||
@@ -188,7 +188,7 @@ mob
|
||||
set name = "5. Stress Test"
|
||||
for(var/i = 0 to 1000)
|
||||
// The third parameter forces it to generate a new one, even if it's already cached
|
||||
getFlatIcon(src,0,2)
|
||||
get_flat_icon(src,0,2)
|
||||
if(prob(5))
|
||||
Add_Overlay()
|
||||
Browse_Icon()
|
||||
@@ -196,7 +196,7 @@ mob
|
||||
Cache_Test()
|
||||
set name = "6. Cache Test"
|
||||
for(var/i = 0 to 1000)
|
||||
getFlatIcon(src)
|
||||
get_flat_icon(src)
|
||||
Browse_Icon()
|
||||
|
||||
/obj/effect/overlayTest
|
||||
@@ -733,7 +733,7 @@ world
|
||||
/mob/proc/AddCamoOverlay(atom/A)//A is the atom which we are using as the overlay.
|
||||
var/icon/opacity_icon = new(A.icon, A.icon_state)//Don't really care for overlays/underlays.
|
||||
//Now we need to culculate overlays+underlays and add them together to form an image for a mask.
|
||||
var/icon/alpha_mask = getIconMask(src)//getFlatIcon(src) is accurate but SLOW. Not designed for running each tick. This is also a little slow since it's blending a bunch of icons together but good enough.
|
||||
var/icon/alpha_mask = getIconMask(src)//get_flat_icon(src) is accurate but SLOW. Not designed for running each tick. This is also a little slow since it's blending a bunch of icons together but good enough.
|
||||
opacity_icon.AddAlphaMask(alpha_mask)//Likely the main source of lag for this proc. Probably not designed to run each tick.
|
||||
opacity_icon.ChangeOpacity(0.4)//Front end for MapColors so it's fast. 0.5 means half opacity and looks the best in my opinion.
|
||||
for(var/i=0,i<5,i++)//And now we add it as overlays. It's faster than creating an icon and then merging it.
|
||||
@@ -878,7 +878,7 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
|
||||
for(var/D in showDirs)
|
||||
body.setDir(D)
|
||||
COMPILE_OVERLAYS(body)
|
||||
var/icon/partial = getFlatIcon(body)
|
||||
var/icon/partial = get_flat_icon(body)
|
||||
out_icon.Insert(partial,dir=D)
|
||||
|
||||
humanoid_icon_cache[icon_id] = out_icon
|
||||
@@ -1038,7 +1038,7 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0
|
||||
|
||||
return "<img class='icon icon-[A.icon_state]' src='data:image/png;base64,[bicon_cache[key]]'>"
|
||||
|
||||
/// Costlier version of icon2html() that uses getFlatIcon() to account for overlays, underlays, etc. Use with extreme moderation, ESPECIALLY on mobs.
|
||||
/// Costlier version of icon2html() that uses get_flat_icon() to account for overlays, underlays, etc. Use with extreme moderation, ESPECIALLY on mobs.
|
||||
/proc/costly_icon2html(thing, target, sourceonly = FALSE)
|
||||
if (!thing)
|
||||
return
|
||||
@@ -1048,7 +1048,7 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0
|
||||
if (isicon(thing))
|
||||
return icon2html(thing, target)
|
||||
|
||||
var/icon/I = getFlatIcon(thing)
|
||||
var/icon/I = get_flat_icon(thing)
|
||||
return icon2html(I, target, sourceonly = sourceonly)
|
||||
|
||||
|
||||
@@ -1103,7 +1103,7 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0
|
||||
/**
|
||||
* Animate a 'halo' around an object.
|
||||
*
|
||||
* This proc is not exactly cheap. You'd be well advised to set up many-loops rather than call this super-often. getCompoundIcon is
|
||||
* This proc is not exactly cheap. You'd be well advised to set up many-loops rather than call this super-often. get_compound_icon is
|
||||
* mostly to blame for this. If Byond ever implements a way to get something's icon more 'gently' than this, do that instead.
|
||||
*
|
||||
* * A - This is the atom to put the halo on
|
||||
@@ -1131,7 +1131,7 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0
|
||||
if(simple_icons)
|
||||
hole = icon(A.icon, A.icon_state)
|
||||
else
|
||||
hole = getCompoundIcon(A)
|
||||
hole = get_compound_icon(A)
|
||||
|
||||
hole.MapColors(0,0,0, 0,0,0, 0,0,0, 1,1,1) //White.
|
||||
|
||||
@@ -1176,7 +1176,7 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0
|
||||
|
||||
// DEPRICATED SOON
|
||||
/proc/downloadImage(atom/A, dir) //this is expensive and dumb
|
||||
var/icon/this_icon = getFlatIcon(A, dir=dir)
|
||||
var/icon/this_icon = get_flat_icon(A, dir=dir)
|
||||
usr << ftp(this_icon,"[A.name].png")
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//? File contains functions to generate flat /icon's from things.
|
||||
//? This is obviously expensive. Very, very expensive.
|
||||
//? new getFlatIcon is faster, but, really, don't use these unless you need to.
|
||||
//? new get_flat_icon is faster, but, really, don't use these unless you need to.
|
||||
//? Chances are unless you are:
|
||||
//? - sending to html/browser (for non character preview purposes)
|
||||
//? - taking photos
|
||||
@@ -14,17 +14,17 @@
|
||||
* - A - appearancelike object.
|
||||
* - no_anim - flatten out animations
|
||||
*/
|
||||
/proc/getCompoundIcon(atom/A, no_anim)
|
||||
/proc/get_compound_icon(atom/A, no_anim)
|
||||
var/mutable_appearance/N = new
|
||||
N.appearance = A
|
||||
N.dir = NORTH
|
||||
var/icon/north = getFlatIcon(N, NORTH, no_anim = no_anim)
|
||||
var/icon/north = get_flat_icon(N, NORTH, no_anim = no_anim)
|
||||
N.dir = SOUTH
|
||||
var/icon/south = getFlatIcon(N, SOUTH, no_anim = no_anim)
|
||||
var/icon/south = get_flat_icon(N, SOUTH, no_anim = no_anim)
|
||||
N.dir = EAST
|
||||
var/icon/east = getFlatIcon(N, EAST, no_anim = no_anim)
|
||||
var/icon/east = get_flat_icon(N, EAST, no_anim = no_anim)
|
||||
N.dir = WEST
|
||||
var/icon/west = getFlatIcon(N, WEST, no_anim = no_anim)
|
||||
var/icon/west = get_flat_icon(N, WEST, no_anim = no_anim)
|
||||
qdel(N)
|
||||
//Starts with a blank icon because of byond bugs.
|
||||
var/icon/full = icon('icons/system/blank_32x32.dmi', "")
|
||||
@@ -38,12 +38,12 @@
|
||||
qdel(west)
|
||||
return full
|
||||
|
||||
/proc/getFlatIcon(appearance/appearancelike, dir, no_anim)
|
||||
/proc/get_flat_icon(appearance/appearancelike, dir, no_anim)
|
||||
if(!dir && isloc(appearancelike))
|
||||
dir = appearancelike.dir
|
||||
return getFlatIcon_new_actual(appearancelike, dir, no_anim, null, TRUE)
|
||||
return get_flat_icon_new_actual(appearancelike, dir, no_anim, null, TRUE)
|
||||
|
||||
/proc/getFlatIcon_new_actual(image/A, defdir, no_anim, deficon, start)
|
||||
/proc/get_flat_icon_new_actual(image/A, defdir, no_anim, deficon, start)
|
||||
// start with blank image
|
||||
var/static/icon/template = icon('icons/system/blank_32x32.dmi', "")
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
if((A.overlays.len + A.underlays.len) > 80)
|
||||
// we use fucking insertion check
|
||||
// > 80 = death.
|
||||
CRASH("getflaticon tried to process more than 80 layers")
|
||||
CRASH("get_flat_icon tried to process more than 80 layers")
|
||||
|
||||
// otherwise, we have to blend in all overlays/underlays.
|
||||
var/icon/flat = BLANK
|
||||
@@ -203,9 +203,9 @@
|
||||
blend_mode = BLEND_OVERLAY
|
||||
adding = icon(icon, state, ourdir)
|
||||
else
|
||||
// use full getflaticon
|
||||
// use full get_flat_icon
|
||||
blend_mode = copying.blend_mode
|
||||
adding = getFlatIcon_new_actual(copying, defdir, no_anim, icon)
|
||||
adding = get_flat_icon_new_actual(copying, defdir, no_anim, icon)
|
||||
|
||||
// if we got nothing, skip
|
||||
if(!adding)
|
||||
@@ -266,223 +266,3 @@
|
||||
#undef INDEX_Y_HIGH
|
||||
|
||||
#undef BLANK
|
||||
|
||||
/*
|
||||
|
||||
--- Why is this code still here?
|
||||
--- Because I don't trust myself, so, I'll leave the old reference code here incase this breaks later.
|
||||
|
||||
/proc/getFlatIcon(A, defdir, no_anim)
|
||||
getFlatIcon_old(A, defdir, null, null, null, null, no_anim)
|
||||
return getFlatIcon_new(A, defdir, no_anim)
|
||||
|
||||
/proc/getFlatIconTest(A, B, C)
|
||||
var/icon/_A = getFlatIcon_old(A, B, null, null, null, null, C)
|
||||
var/icon/_B = getFlatIcon_new(A, B, C)
|
||||
to_chat(world, "[icon2html(_A, world)]")
|
||||
to_chat(world, "[icon2html(_B, world)]")
|
||||
|
||||
// todo: rework
|
||||
// Creates a single icon from a given /atom or /image. Only the first argument is required.
|
||||
/proc/getFlatIcon_old(image/A, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE)
|
||||
//Define... defines.
|
||||
var/static/icon/flat_template = icon('icons/effects/effects.dmi', "nothing")
|
||||
|
||||
#define BLANK icon(flat_template)
|
||||
#define SET_SELF(SETVAR) do { \
|
||||
var/icon/SELF_ICON=icon(icon(curicon, curstate, base_icon_dir),"",SOUTH,no_anim?1:null); \
|
||||
if(A.alpha<255) { \
|
||||
SELF_ICON.Blend(rgb(255,255,255,A.alpha),ICON_MULTIPLY);\
|
||||
} \
|
||||
if(A.color) { \
|
||||
if(islist(A.color)){ \
|
||||
SELF_ICON.MapColors(arglist(A.color))} \
|
||||
else{ \
|
||||
SELF_ICON.Blend(A.color,ICON_MULTIPLY)} \
|
||||
} \
|
||||
##SETVAR=SELF_ICON;\
|
||||
} while (0)
|
||||
|
||||
#define INDEX_X_LOW 1
|
||||
#define INDEX_X_HIGH 2
|
||||
#define INDEX_Y_LOW 3
|
||||
#define INDEX_Y_HIGH 4
|
||||
|
||||
#define flatX1 flat_size[INDEX_X_LOW]
|
||||
#define flatX2 flat_size[INDEX_X_HIGH]
|
||||
#define flatY1 flat_size[INDEX_Y_LOW]
|
||||
#define flatY2 flat_size[INDEX_Y_HIGH]
|
||||
#define addX1 add_size[INDEX_X_LOW]
|
||||
#define addX2 add_size[INDEX_X_HIGH]
|
||||
#define addY1 add_size[INDEX_Y_LOW]
|
||||
#define addY2 add_size[INDEX_Y_HIGH]
|
||||
|
||||
if(!A || A.alpha <= 0)
|
||||
return BLANK
|
||||
|
||||
var/noIcon = FALSE
|
||||
if(start)
|
||||
if(!defdir)
|
||||
defdir = A.dir
|
||||
if(!deficon)
|
||||
deficon = A.icon
|
||||
if(!defstate)
|
||||
defstate = A.icon_state
|
||||
if(!defblend)
|
||||
defblend = A.blend_mode
|
||||
|
||||
var/curicon = A.icon || deficon
|
||||
var/curstate = A.icon_state || defstate
|
||||
|
||||
if(!((noIcon = (!curicon))))
|
||||
var/curstates = icon_states(curicon)
|
||||
if(!(curstate in curstates))
|
||||
if("" in curstates)
|
||||
curstate = ""
|
||||
else
|
||||
noIcon = TRUE // Do not render this object.
|
||||
|
||||
var/curdir
|
||||
var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have
|
||||
|
||||
//These should use the parent's direction (most likely)
|
||||
if(!A.dir || A.dir == SOUTH)
|
||||
curdir = defdir
|
||||
else
|
||||
curdir = A.dir
|
||||
|
||||
//Try to remove/optimize this section ASAP, CPU hog.
|
||||
//Determines if there's directionals.
|
||||
if(!noIcon && curdir != SOUTH)
|
||||
var/exist = FALSE
|
||||
var/static/list/checkdirs = list(NORTH, EAST, WEST)
|
||||
for(var/i in checkdirs) //Not using GLOB for a reason.
|
||||
if(length(icon_states(icon(curicon, curstate, i))))
|
||||
exist = TRUE
|
||||
break
|
||||
if(!exist)
|
||||
base_icon_dir = SOUTH
|
||||
//
|
||||
|
||||
if(!base_icon_dir)
|
||||
base_icon_dir = curdir
|
||||
|
||||
ASSERT(!BLEND_DEFAULT) //I might just be stupid but lets make sure this define is 0.
|
||||
|
||||
var/curblend = A.blend_mode || defblend
|
||||
|
||||
if(A.overlays.len || A.underlays.len)
|
||||
var/icon/flat = BLANK
|
||||
// Layers will be a sorted list of icons/overlays, based on the order in which they are displayed
|
||||
var/list/layers = list()
|
||||
var/image/copy
|
||||
// Add the atom's icon itself, without pixel_x/y offsets.
|
||||
if(!noIcon)
|
||||
copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=base_icon_dir)
|
||||
copy.color = A.color
|
||||
copy.alpha = A.alpha
|
||||
copy.blend_mode = curblend
|
||||
layers[copy] = A.layer
|
||||
|
||||
// Loop through the underlays, then overlays, sorting them into the layers list
|
||||
for(var/process_set in 0 to 1)
|
||||
var/list/process = process_set? A.overlays : A.underlays
|
||||
for(var/i in 1 to process.len)
|
||||
var/image/current = process[i]
|
||||
if(!current)
|
||||
continue
|
||||
if(current.plane != FLOAT_PLANE && current.plane != A.plane)
|
||||
continue
|
||||
var/current_layer = current.layer
|
||||
if(current_layer < 0)
|
||||
if(current_layer <= -1000)
|
||||
return flat
|
||||
current_layer = process_set + A.layer + current_layer / 1000
|
||||
|
||||
for(var/p in 1 to layers.len)
|
||||
var/image/cmp = layers[p]
|
||||
if(current_layer < layers[cmp])
|
||||
layers.Insert(p, current)
|
||||
break
|
||||
layers[current] = current_layer
|
||||
|
||||
//sortTim(layers, /proc/cmp_image_layer_asc)
|
||||
|
||||
var/icon/add // Icon of overlay being added
|
||||
|
||||
// Current dimensions of flattened icon
|
||||
var/list/flat_size = list(1, flat.Width(), 1, flat.Height())
|
||||
// Dimensions of overlay being added
|
||||
var/list/add_size[4]
|
||||
|
||||
for(var/V in layers)
|
||||
var/image/I = V
|
||||
if(I.alpha == 0)
|
||||
continue
|
||||
|
||||
if(I == copy) // 'I' is an /image based on the object being flattened.
|
||||
curblend = BLEND_OVERLAY
|
||||
add = icon(I.icon, I.icon_state, base_icon_dir)
|
||||
else // 'I' is an appearance object.
|
||||
add = getFlatIcon_old(image(I), curdir, curicon, curstate, curblend, FALSE, no_anim)
|
||||
if(!add)
|
||||
continue
|
||||
// Find the new dimensions of the flat icon to fit the added overlay
|
||||
add_size = list(
|
||||
min(flatX1, I.pixel_x+1),
|
||||
max(flatX2, I.pixel_x+add.Width()),
|
||||
min(flatY1, I.pixel_y+1),
|
||||
max(flatY2, I.pixel_y+add.Height())
|
||||
)
|
||||
|
||||
if(flat_size ~! add_size)
|
||||
// Resize the flattened icon so the new icon fits
|
||||
flat.Crop(
|
||||
addX1 - flatX1 + 1,
|
||||
addY1 - flatY1 + 1,
|
||||
addX2 - flatX1 + 1,
|
||||
addY2 - flatY1 + 1
|
||||
)
|
||||
flat_size = add_size.Copy()
|
||||
|
||||
// Blend the overlay into the flattened icon
|
||||
flat.Blend(add, blendMode2iconMode(curblend), I.pixel_x + 2 - flatX1, I.pixel_y + 2 - flatY1)
|
||||
|
||||
if(A.color)
|
||||
if(islist(A.color))
|
||||
flat.MapColors(arglist(A.color))
|
||||
else
|
||||
flat.Blend(A.color, ICON_MULTIPLY)
|
||||
|
||||
if(A.alpha < 255)
|
||||
flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY)
|
||||
|
||||
if(no_anim)
|
||||
//Clean up repeated frames
|
||||
var/icon/cleaned = new /icon()
|
||||
cleaned.Insert(flat, "", SOUTH, 1, 0)
|
||||
. = cleaned
|
||||
else
|
||||
. = icon(flat, "", SOUTH)
|
||||
else //There's no overlays.
|
||||
if(!noIcon)
|
||||
SET_SELF(.)
|
||||
|
||||
//Clear defines
|
||||
#undef flatX1
|
||||
#undef flatX2
|
||||
#undef flatY1
|
||||
#undef flatY2
|
||||
#undef addX1
|
||||
#undef addX2
|
||||
#undef addY1
|
||||
#undef addY2
|
||||
|
||||
#undef INDEX_X_LOW
|
||||
#undef INDEX_X_HIGH
|
||||
#undef INDEX_Y_LOW
|
||||
#undef INDEX_Y_HIGH
|
||||
|
||||
#undef BLANK
|
||||
#undef SET_SELF
|
||||
*/
|
||||
|
||||
219
code/__HELPERS/icons/flatten_old.dm
Normal file
219
code/__HELPERS/icons/flatten_old.dm
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
|
||||
--- Why is this code still here?
|
||||
--- Because I don't trust myself, so, I'll leave the old reference code here incase this breaks later.
|
||||
|
||||
/proc/get_flat_icon(A, defdir, no_anim)
|
||||
get_flat_icon_old(A, defdir, null, null, null, null, no_anim)
|
||||
return get_flat_icon_new(A, defdir, no_anim)
|
||||
|
||||
/proc/get_flat_icon_test(A, B, C)
|
||||
var/icon/_A = get_flat_icon_old(A, B, null, null, null, null, C)
|
||||
var/icon/_B = get_flat_icon_new(A, B, C)
|
||||
to_chat(world, "[icon2html(_A, world)]")
|
||||
to_chat(world, "[icon2html(_B, world)]")
|
||||
|
||||
// todo: rework
|
||||
// Creates a single icon from a given /atom or /image. Only the first argument is required.
|
||||
/proc/get_flat_icon_old(image/A, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE)
|
||||
//Define... defines.
|
||||
var/static/icon/flat_template = icon('icons/effects/effects.dmi', "nothing")
|
||||
|
||||
#define BLANK icon(flat_template)
|
||||
#define SET_SELF(SETVAR) do { \
|
||||
var/icon/SELF_ICON=icon(icon(curicon, curstate, base_icon_dir),"",SOUTH,no_anim?1:null); \
|
||||
if(A.alpha<255) { \
|
||||
SELF_ICON.Blend(rgb(255,255,255,A.alpha),ICON_MULTIPLY);\
|
||||
} \
|
||||
if(A.color) { \
|
||||
if(islist(A.color)){ \
|
||||
SELF_ICON.MapColors(arglist(A.color))} \
|
||||
else{ \
|
||||
SELF_ICON.Blend(A.color,ICON_MULTIPLY)} \
|
||||
} \
|
||||
##SETVAR=SELF_ICON;\
|
||||
} while (0)
|
||||
|
||||
#define INDEX_X_LOW 1
|
||||
#define INDEX_X_HIGH 2
|
||||
#define INDEX_Y_LOW 3
|
||||
#define INDEX_Y_HIGH 4
|
||||
|
||||
#define flatX1 flat_size[INDEX_X_LOW]
|
||||
#define flatX2 flat_size[INDEX_X_HIGH]
|
||||
#define flatY1 flat_size[INDEX_Y_LOW]
|
||||
#define flatY2 flat_size[INDEX_Y_HIGH]
|
||||
#define addX1 add_size[INDEX_X_LOW]
|
||||
#define addX2 add_size[INDEX_X_HIGH]
|
||||
#define addY1 add_size[INDEX_Y_LOW]
|
||||
#define addY2 add_size[INDEX_Y_HIGH]
|
||||
|
||||
if(!A || A.alpha <= 0)
|
||||
return BLANK
|
||||
|
||||
var/noIcon = FALSE
|
||||
if(start)
|
||||
if(!defdir)
|
||||
defdir = A.dir
|
||||
if(!deficon)
|
||||
deficon = A.icon
|
||||
if(!defstate)
|
||||
defstate = A.icon_state
|
||||
if(!defblend)
|
||||
defblend = A.blend_mode
|
||||
|
||||
var/curicon = A.icon || deficon
|
||||
var/curstate = A.icon_state || defstate
|
||||
|
||||
if(!((noIcon = (!curicon))))
|
||||
var/curstates = icon_states(curicon)
|
||||
if(!(curstate in curstates))
|
||||
if("" in curstates)
|
||||
curstate = ""
|
||||
else
|
||||
noIcon = TRUE // Do not render this object.
|
||||
|
||||
var/curdir
|
||||
var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have
|
||||
|
||||
//These should use the parent's direction (most likely)
|
||||
if(!A.dir || A.dir == SOUTH)
|
||||
curdir = defdir
|
||||
else
|
||||
curdir = A.dir
|
||||
|
||||
//Try to remove/optimize this section ASAP, CPU hog.
|
||||
//Determines if there's directionals.
|
||||
if(!noIcon && curdir != SOUTH)
|
||||
var/exist = FALSE
|
||||
var/static/list/checkdirs = list(NORTH, EAST, WEST)
|
||||
for(var/i in checkdirs) //Not using GLOB for a reason.
|
||||
if(length(icon_states(icon(curicon, curstate, i))))
|
||||
exist = TRUE
|
||||
break
|
||||
if(!exist)
|
||||
base_icon_dir = SOUTH
|
||||
//
|
||||
|
||||
if(!base_icon_dir)
|
||||
base_icon_dir = curdir
|
||||
|
||||
ASSERT(!BLEND_DEFAULT) //I might just be stupid but lets make sure this define is 0.
|
||||
|
||||
var/curblend = A.blend_mode || defblend
|
||||
|
||||
if(A.overlays.len || A.underlays.len)
|
||||
var/icon/flat = BLANK
|
||||
// Layers will be a sorted list of icons/overlays, based on the order in which they are displayed
|
||||
var/list/layers = list()
|
||||
var/image/copy
|
||||
// Add the atom's icon itself, without pixel_x/y offsets.
|
||||
if(!noIcon)
|
||||
copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=base_icon_dir)
|
||||
copy.color = A.color
|
||||
copy.alpha = A.alpha
|
||||
copy.blend_mode = curblend
|
||||
layers[copy] = A.layer
|
||||
|
||||
// Loop through the underlays, then overlays, sorting them into the layers list
|
||||
for(var/process_set in 0 to 1)
|
||||
var/list/process = process_set? A.overlays : A.underlays
|
||||
for(var/i in 1 to process.len)
|
||||
var/image/current = process[i]
|
||||
if(!current)
|
||||
continue
|
||||
if(current.plane != FLOAT_PLANE && current.plane != A.plane)
|
||||
continue
|
||||
var/current_layer = current.layer
|
||||
if(current_layer < 0)
|
||||
if(current_layer <= -1000)
|
||||
return flat
|
||||
current_layer = process_set + A.layer + current_layer / 1000
|
||||
|
||||
for(var/p in 1 to layers.len)
|
||||
var/image/cmp = layers[p]
|
||||
if(current_layer < layers[cmp])
|
||||
layers.Insert(p, current)
|
||||
break
|
||||
layers[current] = current_layer
|
||||
|
||||
//tim_sort(layers, /proc/cmp_image_layer_asc)
|
||||
|
||||
var/icon/add // Icon of overlay being added
|
||||
|
||||
// Current dimensions of flattened icon
|
||||
var/list/flat_size = list(1, flat.Width(), 1, flat.Height())
|
||||
// Dimensions of overlay being added
|
||||
var/list/add_size[4]
|
||||
|
||||
for(var/V in layers)
|
||||
var/image/I = V
|
||||
if(I.alpha == 0)
|
||||
continue
|
||||
|
||||
if(I == copy) // 'I' is an /image based on the object being flattened.
|
||||
curblend = BLEND_OVERLAY
|
||||
add = icon(I.icon, I.icon_state, base_icon_dir)
|
||||
else // 'I' is an appearance object.
|
||||
add = get_flat_icon_old(image(I), curdir, curicon, curstate, curblend, FALSE, no_anim)
|
||||
if(!add)
|
||||
continue
|
||||
// Find the new dimensions of the flat icon to fit the added overlay
|
||||
add_size = list(
|
||||
min(flatX1, I.pixel_x+1),
|
||||
max(flatX2, I.pixel_x+add.Width()),
|
||||
min(flatY1, I.pixel_y+1),
|
||||
max(flatY2, I.pixel_y+add.Height())
|
||||
)
|
||||
|
||||
if(flat_size ~! add_size)
|
||||
// Resize the flattened icon so the new icon fits
|
||||
flat.Crop(
|
||||
addX1 - flatX1 + 1,
|
||||
addY1 - flatY1 + 1,
|
||||
addX2 - flatX1 + 1,
|
||||
addY2 - flatY1 + 1
|
||||
)
|
||||
flat_size = add_size.Copy()
|
||||
|
||||
// Blend the overlay into the flattened icon
|
||||
flat.Blend(add, blendMode2iconMode(curblend), I.pixel_x + 2 - flatX1, I.pixel_y + 2 - flatY1)
|
||||
|
||||
if(A.color)
|
||||
if(islist(A.color))
|
||||
flat.MapColors(arglist(A.color))
|
||||
else
|
||||
flat.Blend(A.color, ICON_MULTIPLY)
|
||||
|
||||
if(A.alpha < 255)
|
||||
flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY)
|
||||
|
||||
if(no_anim)
|
||||
//Clean up repeated frames
|
||||
var/icon/cleaned = new /icon()
|
||||
cleaned.Insert(flat, "", SOUTH, 1, 0)
|
||||
. = cleaned
|
||||
else
|
||||
. = icon(flat, "", SOUTH)
|
||||
else //There's no overlays.
|
||||
if(!noIcon)
|
||||
SET_SELF(.)
|
||||
|
||||
//Clear defines
|
||||
#undef flatX1
|
||||
#undef flatX2
|
||||
#undef flatY1
|
||||
#undef flatY2
|
||||
#undef addX1
|
||||
#undef addX2
|
||||
#undef addY1
|
||||
#undef addY2
|
||||
|
||||
#undef INDEX_X_LOW
|
||||
#undef INDEX_X_HIGH
|
||||
#undef INDEX_Y_LOW
|
||||
#undef INDEX_Y_HIGH
|
||||
|
||||
#undef BLANK
|
||||
#undef SET_SELF
|
||||
*/
|
||||
@@ -6,33 +6,14 @@
|
||||
GLOBAL_LIST(string_cache)
|
||||
GLOBAL_VAR(string_filename_current_key)
|
||||
|
||||
|
||||
/proc/strings_replacement(filepath, key)
|
||||
filepath = sanitize_filepath(filepath)
|
||||
load_strings_file(filepath)
|
||||
|
||||
if((filepath in GLOB.string_cache) && (key in GLOB.string_cache[filepath]))
|
||||
var/response = pick(GLOB.string_cache[filepath][key])
|
||||
var/regex/r = regex("@pick\\((\\D+?)\\)", "g")
|
||||
response = r.Replace(response, /proc/strings_subkey_lookup)
|
||||
return response
|
||||
else
|
||||
CRASH("strings list not found: [STRING_DIRECTORY]/[filepath], index=[key]")
|
||||
|
||||
/proc/strings(filepath as text, key as text, directory = STRING_DIRECTORY)
|
||||
if(IsAdminAdvancedProcCall())
|
||||
return
|
||||
|
||||
filepath = sanitize_filepath(filepath)
|
||||
load_strings_file(filepath, directory)
|
||||
if((filepath in GLOB.string_cache) && (key in GLOB.string_cache[filepath]))
|
||||
return GLOB.string_cache[filepath][key]
|
||||
else
|
||||
CRASH("strings list not found: [directory]/[filepath], index=[key]")
|
||||
|
||||
/proc/strings_subkey_lookup(match, group1)
|
||||
return pick_list(GLOB.string_filename_current_key, group1)
|
||||
|
||||
/**
|
||||
* ? Load and cache a json file into a list.
|
||||
* ! Note If the file is already cached, it will CRASH
|
||||
*
|
||||
* * @param filename The filename to load.
|
||||
* * @param directory The directory to load from.
|
||||
* * @return Nothing.
|
||||
*/
|
||||
/proc/load_strings_file(filepath, directory = STRING_DIRECTORY)
|
||||
if(IsAdminAdvancedProcCall())
|
||||
return
|
||||
@@ -48,3 +29,52 @@ GLOBAL_VAR(string_filename_current_key)
|
||||
GLOB.string_cache[filepath] = json_load("[directory]/[filepath]")
|
||||
else
|
||||
CRASH("file not found: [directory]/[filepath]")
|
||||
|
||||
/**
|
||||
* ? Take in a JSON file and a target property (must be an array), and search for the property in the file.
|
||||
* ? If the property is a string, return it.
|
||||
*
|
||||
* * @param filename The filename to load.
|
||||
* * @param key The key to search for.
|
||||
* * @return The value of the key, or null if not found.
|
||||
*/
|
||||
/proc/strings(filepath as text, key as text, directory = STRING_DIRECTORY)
|
||||
if(IsAdminAdvancedProcCall())
|
||||
return
|
||||
|
||||
filepath = sanitize_filepath(filepath)
|
||||
load_strings_file(filepath, directory)
|
||||
if((filepath in GLOB.string_cache) && (key in GLOB.string_cache[filepath]))
|
||||
return GLOB.string_cache[filepath][key]
|
||||
else
|
||||
CRASH("strings list not found: [directory]/[filepath], index=[key]")
|
||||
|
||||
/**
|
||||
* Macro Proc for /proc/strings_replacement(filepath, key)
|
||||
*/
|
||||
/proc/strings_subkey_lookup(match, group1)
|
||||
return pick_list(GLOB.string_filename_current_key, group1)
|
||||
|
||||
/**
|
||||
*? This function parses a json file and returns a string.
|
||||
** @param filename The filename of the json file.
|
||||
** @param key The key of the string to return.
|
||||
** @return The string.
|
||||
*
|
||||
*
|
||||
* Further documentation:
|
||||
*- Pick a random string from the chosen array in the json file.
|
||||
*- Find and replaces all the @pick(VALUE) with a random string from the VALUE array.
|
||||
*- Return final string.
|
||||
*/
|
||||
/proc/strings_replacement(filepath, key)
|
||||
filepath = sanitize_filepath(filepath)
|
||||
load_strings_file(filepath)
|
||||
|
||||
if((filepath in GLOB.string_cache) && (key in GLOB.string_cache[filepath]))
|
||||
var/response = pick(GLOB.string_cache[filepath][key])
|
||||
var/regex/r = regex("@pick\\((\\D+?)\\)", "g")
|
||||
response = r.Replace(response, /proc/strings_subkey_lookup)
|
||||
return response
|
||||
else
|
||||
CRASH("strings list not found: [STRING_DIRECTORY]/[filepath], index=[key]")
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
#error contents via list.values, if that is true remove this
|
||||
#error otherwise, update the version and bug lummox
|
||||
#endif
|
||||
//Flattens a keyed list into a list of it's contents
|
||||
|
||||
/**
|
||||
* Flattens a keyed list into a list of it's contents.
|
||||
*/
|
||||
/proc/flatten_list_nodupe(list/key_list)
|
||||
if(!islist(key_list))
|
||||
return null
|
||||
@@ -47,8 +50,12 @@
|
||||
L["[key]"] = "[value]"
|
||||
return list2params(L)
|
||||
|
||||
//takes an input_key, as text, and the list of keys already used, outputting a replacement key in the format of "[input_key] ([number_of_duplicates])" if it finds a duplicate
|
||||
//use this for lists of things that might have the same name, like mobs or objects, that you plan on giving to a player as input
|
||||
/**
|
||||
* Takes an input_key, as text, and the list of keys already used, outputting a replacement key
|
||||
* in the format of "[input_key] ([number_of_duplicates])" if it finds a duplicate use this for
|
||||
* lists of things that might have the same name, like mobs or objects, that you plan on giving
|
||||
* to a player as input.
|
||||
*/
|
||||
/proc/avoid_assoc_duplicate_keys(input_key, list/used_key_list)
|
||||
if(!input_key || !istype(used_key_list))
|
||||
return
|
||||
@@ -59,8 +66,10 @@
|
||||
used_key_list[input_key] = 1
|
||||
return input_key
|
||||
|
||||
// Return a list of the values in an assoc list (including null)
|
||||
/proc/list_values(var/list/L)
|
||||
/**
|
||||
* Return a list of the values in an assoc list (including null)
|
||||
*/
|
||||
/proc/list_values(list/L)
|
||||
. = list()
|
||||
for(var/e in L)
|
||||
. += L[e]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//Copies a list, and all lists inside it recusively
|
||||
//Does not copy any other reference type
|
||||
/**
|
||||
* Copies a list, and all lists inside it recusively.
|
||||
* Does not copy any other reference type.
|
||||
*/
|
||||
/proc/deepCopyList(list/l)
|
||||
if(!islist(l))
|
||||
return l
|
||||
@@ -7,7 +9,7 @@
|
||||
for(var/i = 1 to l.len)
|
||||
var/key = .[i]
|
||||
if(isnum(key))
|
||||
// numbers cannot ever be associative keys
|
||||
//! Numbers cannot ever be associative keys.
|
||||
continue
|
||||
var/value = .[key]
|
||||
if(islist(value))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* Definining a counter as a series of key -> numeric value entries
|
||||
|
||||
/** Definining a counter as a series of key -> numeric value entries
|
||||
*
|
||||
* All these procs modify in place.
|
||||
*/
|
||||
*/
|
||||
|
||||
/proc/counterlist_scale(list/L, scalar)
|
||||
var/list/out = list()
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
//Converts a bitfield to a list of numbers (or words if a wordlist is provided)
|
||||
/**
|
||||
* Converts a bitfield to a list of numbers (or words if a wordlist is provided)
|
||||
*/
|
||||
/proc/bitfield2list(bitfield = NONE, list/wordlist)
|
||||
var/list/r = list()
|
||||
if(islist(wordlist))
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
//Picks a random element from a list based on a weighting system:
|
||||
//1. Adds up the total of weights for each element
|
||||
//2. Gets a number between 1 and that total
|
||||
//3. For each element in the list, subtracts its weighting from that number
|
||||
//4. If that makes the number 0 or less, return that element.
|
||||
/**
|
||||
* Picks a random element from a list based on a weighting system:
|
||||
* - 1. Adds up the total of weights for each element.
|
||||
* - 2. Gets a number between 1 and that total.
|
||||
* - 3. For each element in the list, subtracts its weighting from that number.
|
||||
* - 4. If that makes the number 0 or less, return that element.
|
||||
*/
|
||||
/proc/pickweight(list/L)
|
||||
var/total = 0
|
||||
var/item
|
||||
@@ -19,7 +21,11 @@
|
||||
|
||||
return null
|
||||
|
||||
/proc/pickweightAllowZero(list/L) //The original pickweight proc will sometimes pick entries with zero weight. I'm not sure if changing the original will break anything, so I left it be.
|
||||
/**
|
||||
* The original pickweight proc will sometimes pick entries with zero weight.
|
||||
* I'm not sure if changing the original will break anything, so I left it be.
|
||||
*/
|
||||
/proc/pickweightAllowZero(list/L)
|
||||
var/total = 0
|
||||
var/item
|
||||
for (item in L)
|
||||
@@ -35,10 +41,13 @@
|
||||
|
||||
return null
|
||||
|
||||
//Pick a random element from the list and remove it from the list.
|
||||
/**
|
||||
* Pick a random element from the list and remove it from the list.
|
||||
*/
|
||||
/proc/pick_n_take(list/L)
|
||||
RETURN_TYPE(L[_].type)
|
||||
if(L.len)
|
||||
var/picked = rand(1,L.len)
|
||||
. = L[picked]
|
||||
L.Cut(picked,picked+1) //Cut is far more efficient that Remove()
|
||||
// Cut is far more efficient that Remove()
|
||||
L.Cut(picked,picked+1)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
//Returns the top(last) element from the list and removes it from the list (typical stack function)
|
||||
/**
|
||||
* Returns the top(last) element from the list and removes it from the list (typical stack function)
|
||||
*/
|
||||
/proc/pop(list/L)
|
||||
if(L.len)
|
||||
. = L[L.len]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//Randomize: Return the list in a random order
|
||||
/proc/shuffle(var/list/L)
|
||||
/**
|
||||
*
|
||||
*/
|
||||
/proc/shuffle(list/L)
|
||||
if(!L)
|
||||
return
|
||||
|
||||
@@ -9,7 +11,9 @@
|
||||
L.Swap(i, rand(i,L.len))
|
||||
return L
|
||||
|
||||
//same, but returns nothing and acts on list in place
|
||||
/**
|
||||
* Randomize: Returns nothing and acts on list in place.
|
||||
*/
|
||||
/proc/shuffle_inplace(list/L)
|
||||
if(!L)
|
||||
return
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
///for sorting clients or mobs by ckey
|
||||
/**
|
||||
* For sorting clients or mobs by ckey.
|
||||
*/
|
||||
/proc/sort_key(list/ckey_list, order=1)
|
||||
return sortTim(ckey_list, order >= 0 ? /proc/cmp_ckey_asc : /proc/cmp_ckey_dsc)
|
||||
return tim_sort(ckey_list, order >= 0 ? /proc/cmp_ckey_asc : /proc/cmp_ckey_dsc)
|
||||
|
||||
///Specifically for record datums in a list.
|
||||
/**
|
||||
* Specifically for record datums in a list.
|
||||
*/
|
||||
/proc/sort_record(list/record_list, field = "name", order = 1)
|
||||
GLOB.cmp_field = field
|
||||
return sortTim(record_list, order >= 0 ? /proc/cmp_records_asc : /proc/cmp_records_dsc)
|
||||
return tim_sort(record_list, order >= 0 ? /proc/cmp_records_asc : /proc/cmp_records_dsc)
|
||||
|
||||
///sort any value in a list
|
||||
/**
|
||||
* Sort any value in a list.
|
||||
*/
|
||||
/proc/sort_list(list/list_to_sort, cmp=/proc/cmp_text_asc)
|
||||
return sortTim(list_to_sort.Copy(), cmp)
|
||||
return tim_sort(list_to_sort.Copy(), cmp)
|
||||
|
||||
///uses sort_list() but uses the var's name specifically. This should probably be using mergeAtom() instead
|
||||
/**
|
||||
* Uses sort_list() but uses the var's name specifically.
|
||||
* This should probably be using mergeAtom() instead.
|
||||
*/
|
||||
/proc/sort_names(list/list_to_sort, order=1)
|
||||
return sortTim(list_to_sort.Copy(), order >= 0 ? /proc/cmp_name_asc : /proc/cmp_name_dsc)
|
||||
return tim_sort(list_to_sort.Copy(), order >= 0 ? /proc/cmp_name_asc : /proc/cmp_name_dsc)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
//Returns a list in plain english as a string
|
||||
/**
|
||||
* Returns a list in plain english as a string.
|
||||
*/
|
||||
/proc/english_list(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" )
|
||||
var/total = input.len
|
||||
if (!total)
|
||||
@@ -19,7 +21,9 @@
|
||||
|
||||
return "[output][and_text][input[index]]"
|
||||
|
||||
//Removes a string from a list
|
||||
/**
|
||||
* Removes a string from a list.
|
||||
*/
|
||||
/proc/remove_strings_from_list(string, list/L)
|
||||
if(!LAZYLEN(L) || !string)
|
||||
return
|
||||
|
||||
@@ -13,7 +13,9 @@ GLOBAL_LIST_EMPTY(string_lists)
|
||||
|
||||
return GLOB.string_lists[string_id] = values
|
||||
|
||||
///A wrapper for baseturf string lists, to offer support of non list values, and a stack_trace if we have major issues
|
||||
/**
|
||||
* A wrapper for baseturf string lists, to offer support of non list values, and a stack_trace if we have major issues.
|
||||
*/
|
||||
/proc/baseturfs_string_list(list/values, turf/baseturf_holder)
|
||||
if(!islist(values))
|
||||
return values //baseturf things
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// Returns the next item in a list
|
||||
/**
|
||||
* Returns the next item in a list.
|
||||
*/
|
||||
/proc/next_list_item(item, list/L)
|
||||
var/i
|
||||
i = L.Find(item)
|
||||
@@ -8,7 +10,9 @@
|
||||
i++
|
||||
return L[i]
|
||||
|
||||
// Returns the previous item in a list
|
||||
/**
|
||||
* Returns the previous item in a list.
|
||||
*/
|
||||
/proc/previous_list_item(item, list/L)
|
||||
var/i
|
||||
i = L.Find(item)
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
//Checks for specific types in a list
|
||||
/**
|
||||
* Checks for specific types in a list.
|
||||
*/
|
||||
/proc/is_type_in_list(atom/A, list/L)
|
||||
for(var/type in L)
|
||||
if(istype(A, type))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//Checks for specific types in a list
|
||||
/**
|
||||
* Checks for specific types in a list.
|
||||
*/
|
||||
/proc/is_path_in_list(path, list/L)
|
||||
for(var/P in L)
|
||||
if(ispath(path, P))
|
||||
@@ -15,12 +19,19 @@
|
||||
/proc/subtypesof(prototype)
|
||||
return (typesof(prototype) - prototype)
|
||||
|
||||
//Typecaches, specially formatted lists used to check for types with priority to speed rather than memory efficiency.
|
||||
/**
|
||||
*! Typecaches,
|
||||
*? Specially formatted lists used to check for types with priority to speed rather than memory efficiency.
|
||||
*/
|
||||
|
||||
//Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches')
|
||||
/**
|
||||
* Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches')
|
||||
*/
|
||||
#define is_type_in_typecache(A, L) (A && length(L) && L[(ispath(A) ? A : A:type)])
|
||||
|
||||
//returns a new list with only atoms that are in typecache L
|
||||
/**
|
||||
* Returns a new list with only atoms that are in typecache L.
|
||||
*/
|
||||
/proc/typecache_filter_list(list/atoms, list/typecache)
|
||||
RETURN_TYPE(/list)
|
||||
. = list()
|
||||
@@ -41,7 +52,9 @@
|
||||
if(typecache_include[A.type] && !typecache_exclude[A.type])
|
||||
. += A
|
||||
|
||||
//Like typesof() or subtypesof(), but returns a typecache instead of a list
|
||||
/**
|
||||
* Like typesof() or subtypesof(), but returns a typecache instead of a list.
|
||||
*/
|
||||
/proc/typecacheof(path, ignore_root_path, only_root_path = FALSE)
|
||||
if(ispath(path))
|
||||
var/list/types = list()
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
//Return a list with no duplicate entries
|
||||
/proc/uniqueList(var/list/L)
|
||||
/**
|
||||
* Return a list with no duplicate entries.
|
||||
*/
|
||||
/proc/uniqueList(list/L)
|
||||
. = list()
|
||||
for(var/i in L)
|
||||
. |= i
|
||||
|
||||
//same, but returns nothing and acts on list in place (also handles associated values properly)
|
||||
/**
|
||||
* Returns nothing and acts on list in place (also handles associated values properly)
|
||||
*/
|
||||
/proc/uniqueList_inplace(list/L)
|
||||
var/temp = L.Copy()
|
||||
L.len = 0
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// COLOUR MATRICES //
|
||||
/////////////////////
|
||||
|
||||
/* Documenting a couple of potentially useful color matrices here to inspire ideas
|
||||
/* Documenting a couple of potentially useful color matrices here to inspire ideas.
|
||||
// Greyscale - indentical to saturation @ 0
|
||||
list(LUMA_R,LUMA_R,LUMA_R,0, LUMA_G,LUMA_G,LUMA_G,0, LUMA_B,LUMA_B,LUMA_B,0, 0,0,0,1, 0,0,0,0)
|
||||
|
||||
@@ -13,17 +13,21 @@ list(-1,0,0,0, 0,-1,0,0, 0,0,-1,0, 0,0,0,1, 1,1,1,0)
|
||||
list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0,0,0)
|
||||
*/
|
||||
|
||||
//Does nothing
|
||||
/// Does nothing.
|
||||
/proc/color_matrix_identity()
|
||||
return list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0)
|
||||
|
||||
//Adds/subtracts overall lightness
|
||||
//0 is identity, 1 makes everything white, -1 makes everything black
|
||||
/**
|
||||
* Adds/subtracts overall lightness.
|
||||
* 0 is identity, 1 makes everything white, -1 makes everything black.
|
||||
*/
|
||||
/proc/color_matrix_lightness(power)
|
||||
return list(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1, power,power,power,0)
|
||||
|
||||
//Changes distance hues have from grey while maintaining the overall lightness. Greys are unaffected.
|
||||
//1 is identity, 0 is greyscale, >1 oversaturates colors
|
||||
/**
|
||||
* Changes distance hues have from grey while maintaining the overall lightness. Greys are unaffected.
|
||||
* 1 is identity, 0 is greyscale, >1 oversaturates colors.
|
||||
*/
|
||||
/proc/color_matrix_saturation(value)
|
||||
var/inv = 1 - value
|
||||
var/R = round(LUMA_R * inv, 0.001)
|
||||
@@ -33,7 +37,7 @@ list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0
|
||||
return list(R + value,R,R,0, G,G + value,G,0, B,B,B + value,0, 0,0,0,1, 0,0,0,0)
|
||||
|
||||
/**
|
||||
* Exxagerates or removes colors
|
||||
* Exxagerates or removes colors.
|
||||
*/
|
||||
/proc/color_matrix_saturation_percent(percent)
|
||||
if(percent == 0)
|
||||
@@ -50,19 +54,21 @@ list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0
|
||||
return list(R + x,R,R, G,G + x,G, B,B,B + x)
|
||||
|
||||
/**
|
||||
* greyscale matrix
|
||||
* Greyscale matrix.
|
||||
*/
|
||||
/proc/color_matrix_greyscale()
|
||||
return list(LUMA_R, LUMA_R, LUMA_R, LUMA_G, LUMA_G, LUMA_G, LUMA_B, LUMA_B, LUMA_B)
|
||||
|
||||
//Changes distance colors have from rgb(127,127,127) grey
|
||||
//1 is identity. 0 makes everything grey >1 blows out colors and greys
|
||||
/**
|
||||
* Changes distance colors have from rgb(127,127,127) grey.
|
||||
* 1 is identity. 0 makes everything grey >1 blows out colors and greys.
|
||||
*/
|
||||
/proc/color_matrix_contrast(value)
|
||||
var/add = (1 - value) / 2
|
||||
return list(value,0,0,0, 0,value,0,0, 0,0,value,0, 0,0,0,1, add,add,add,0)
|
||||
|
||||
/**
|
||||
* Exxagerates or removes brightness
|
||||
* Exxagerates or removes brightness.
|
||||
*/
|
||||
/proc/color_matrix_contrast_percent(percent)
|
||||
var/static/list/delta_index = list(
|
||||
@@ -96,22 +102,27 @@ list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0
|
||||
var/add = 0.5 * (127-x) / 255
|
||||
return list(mult,0,0, 0,mult,0, 0,0,mult, add,add,add)
|
||||
|
||||
//Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting greys
|
||||
//0 is identity, 120 moves reds to greens, 240 moves reds to blues
|
||||
/**
|
||||
* Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting greys.
|
||||
* 0 is identity, 120 moves reds to greens, 240 moves reds to blues.
|
||||
*/
|
||||
//
|
||||
//
|
||||
/proc/color_matrix_rotate_hue(angle)
|
||||
var/sin = sin(angle)
|
||||
var/cos = cos(angle)
|
||||
var/cos_inv_third = 0.333*(1-cos)
|
||||
var/sqrt3_sin = sqrt(3)*sin
|
||||
return list(
|
||||
round(cos+cos_inv_third, 0.001), round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), 0,
|
||||
round(cos_inv_third-sqrt3_sin, 0.001), round(cos+cos_inv_third, 0.001), round(cos_inv_third+sqrt3_sin, 0.001), 0,
|
||||
round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), round(cos+cos_inv_third, 0.001), 0,
|
||||
0,0,0,1,
|
||||
0,0,0,0)
|
||||
round(cos+cos_inv_third, 0.001), round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), 0,
|
||||
round(cos_inv_third-sqrt3_sin, 0.001), round(cos+cos_inv_third, 0.001), round(cos_inv_third+sqrt3_sin, 0.001), 0,
|
||||
round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), round(cos+cos_inv_third, 0.001), 0,
|
||||
0,0,0,1,
|
||||
0,0,0,0,
|
||||
)
|
||||
|
||||
/**
|
||||
* Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting whites
|
||||
* Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting whites.
|
||||
* TODO: Need a version that only affects one color (ie shift red to blue but leave greens and blues alone)
|
||||
*/
|
||||
/proc/color_matrix_rotation(angle)
|
||||
@@ -130,18 +141,23 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro
|
||||
LUMA_B + cos * -LUMA_B + sin * (1-LUMA_B), LUMA_B + cos * -LUMA_B + sin * constC, LUMA_B + cos * (1-LUMA_B) + sin * LUMA_B
|
||||
)
|
||||
|
||||
//These next three rotate values about one axis only
|
||||
//x is the red axis, y is the green axis, z is the blue axis.
|
||||
/**
|
||||
* These next three rotate values about one axis only.
|
||||
* x is the red axis, y is the green axis, z is the blue axis.
|
||||
*/
|
||||
/proc/color_matrix_rotate_x(angle)
|
||||
var/sinval = round(sin(angle), 0.001); var/cosval = round(cos(angle), 0.001)
|
||||
var/sinval = round(sin(angle), 0.001)
|
||||
var/cosval = round(cos(angle), 0.001)
|
||||
return list(1,0,0,0, 0,cosval,sinval,0, 0,-sinval,cosval,0, 0,0,0,1, 0,0,0,0)
|
||||
|
||||
/proc/color_matrix_rotate_y(angle)
|
||||
var/sinval = round(sin(angle), 0.001); var/cosval = round(cos(angle), 0.001)
|
||||
var/sinval = round(sin(angle), 0.001)
|
||||
var/cosval = round(cos(angle), 0.001)
|
||||
return list(cosval,0,-sinval,0, 0,1,0,0, sinval,0,cosval,0, 0,0,0,1, 0,0,0,0)
|
||||
|
||||
/proc/color_matrix_rotate_z(angle)
|
||||
var/sinval = round(sin(angle), 0.001); var/cosval = round(cos(angle), 0.001)
|
||||
var/sinval = round(sin(angle), 0.001)
|
||||
var/cosval = round(cos(angle), 0.001)
|
||||
return list(cosval,sinval,0,0, -sinval,cosval,0,0, 0,0,1,0, 0,0,0,1, 0,0,0,0)
|
||||
|
||||
/**
|
||||
@@ -164,7 +180,9 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro
|
||||
0, 0, 0
|
||||
)
|
||||
|
||||
//Returns a matrix addition of A with B
|
||||
/**
|
||||
* Returns a matrix addition of A with B.
|
||||
*/
|
||||
/proc/color_matrix_add(list/A, list/B)
|
||||
if(!istype(A) || !istype(B))
|
||||
return color_matrix_identity()
|
||||
@@ -176,7 +194,9 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro
|
||||
output[value] = A[value] + B[value]
|
||||
return output
|
||||
|
||||
//Returns a matrix multiplication of A with B
|
||||
/**
|
||||
* Returns a matrix multiplication of A with B.
|
||||
*/
|
||||
/proc/color_matrix_multiply(list/A, list/B)
|
||||
if(!istype(A) || !istype(B))
|
||||
return color_matrix_identity()
|
||||
@@ -194,20 +214,20 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro
|
||||
return output
|
||||
|
||||
/**
|
||||
* Assembles a color matrix, defaulting to identity
|
||||
* Assembles a color matrix, defaulting to identity.
|
||||
*/
|
||||
/proc/rgb_construct_color_matrix(rr = 1, rg, rb, gr, gg = 1, gb, br, bg, bb = 1, cr, cg, cb)
|
||||
return list(rr, rg, rb, gr, gg, gb, br, bg, bb, cr, cg, cb)
|
||||
|
||||
/**
|
||||
* Assembles a color matrix, defaulting to identity
|
||||
* Assembles a color matrix, defaulting to identity.
|
||||
*/
|
||||
/proc/rgba_construct_color_matrix(rr = 1, rg, rb, ra, gr, gg = 1, gb, ga, br, bg, bb = 1, ba, ar, ag, ab, aa = 1, cr, cg, cb, ca)
|
||||
return list(rr, rg, rb, ra, gr, gg, gb, ga, br, bg, bb, ba, ar, ag, ab, aa, cr, cg, cb, ca)
|
||||
|
||||
/**
|
||||
* constructs a colored greyscale matrix
|
||||
* warning: bad math up ahead
|
||||
* Constructs a colored greyscale matrix.
|
||||
* WARNING: Bad math up ahead.
|
||||
*/
|
||||
/proc/rgba_auto_greyscale_matrix(rgba_string)
|
||||
// process rgb(a)
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
//doesn't have an object argument because this is "Stacking" with the animate call above
|
||||
//3 billion% intentional
|
||||
|
||||
//Dumps the matrix data in format a-f
|
||||
/**
|
||||
* Dumps the matrix data in format a-f.
|
||||
*/
|
||||
/matrix/proc/tolist()
|
||||
. = list()
|
||||
. += a
|
||||
@@ -38,12 +40,13 @@
|
||||
. += e
|
||||
. += f
|
||||
|
||||
//Dumps the matrix data in a matrix-grid format
|
||||
/*
|
||||
a d 0
|
||||
b e 0
|
||||
c f 1
|
||||
*/
|
||||
/**
|
||||
* Dumps the matrix data in a matrix-grid format.
|
||||
*
|
||||
* a d 0
|
||||
* b e 0
|
||||
* c f 1
|
||||
*/
|
||||
/matrix/proc/togrid()
|
||||
. = list()
|
||||
. += a
|
||||
@@ -56,11 +59,15 @@
|
||||
. += f
|
||||
. += 1
|
||||
|
||||
//The X pixel offset of this matrix
|
||||
/**
|
||||
* The X pixel offset of this matrix.
|
||||
*/
|
||||
/matrix/proc/get_x_shift()
|
||||
. = c
|
||||
|
||||
//The Y pixel offset of this matrix
|
||||
/**
|
||||
* The Y pixel offset of this matrix.
|
||||
*/
|
||||
/matrix/proc/get_y_shift()
|
||||
. = f
|
||||
|
||||
@@ -70,17 +77,19 @@
|
||||
/matrix/proc/get_y_skew()
|
||||
. = d
|
||||
|
||||
//Skews a matrix in a particular direction
|
||||
//Missing arguments are treated as no skew in that direction
|
||||
|
||||
//As Rotation is defined as a scale+skew, these procs will break any existing rotation
|
||||
//Unless the result is multiplied against the current matrix
|
||||
/**
|
||||
* Skews a matrix in a particular direction.
|
||||
* Missing arguments are treated as no skew in that direction.
|
||||
*
|
||||
* As Rotation is defined as a scale+skew, these procs will break any existing rotation.
|
||||
* Unless the result is multiplied against the current matrix.
|
||||
*/
|
||||
/matrix/proc/set_skew(x = 0, y = 0)
|
||||
b = x
|
||||
d = y
|
||||
|
||||
/**
|
||||
* constructs a transform matrix, defaulting to identity
|
||||
* Constructs a transform matrix, defaulting to identity.
|
||||
*/
|
||||
/proc/transform_matrix_construct(a = 1, b, c, d = 1, e, f)
|
||||
return matrix(a, b, c, d, e, f)
|
||||
|
||||
@@ -87,9 +87,9 @@
|
||||
|
||||
if(!current_species || current_species.name_language == null)
|
||||
if(gender==FEMALE)
|
||||
return capitalize(pick(first_names_female)) + " " + capitalize(pick(last_names))
|
||||
return capitalize(pick(GLOB.first_names_female)) + " " + capitalize(pick(GLOB.last_names))
|
||||
else
|
||||
return capitalize(pick(first_names_male)) + " " + capitalize(pick(last_names))
|
||||
return capitalize(pick(GLOB.first_names_male)) + " " + capitalize(pick(GLOB.last_names))
|
||||
else
|
||||
return current_species.get_random_name(gender)
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
if(cached_character_icons[cachekey])
|
||||
. = cached_character_icons[cachekey]
|
||||
else
|
||||
. = getCompoundIcon(desired)
|
||||
. = get_compound_icon(desired)
|
||||
cached_character_icons[cachekey] = .
|
||||
|
||||
/// Gets the client of the mob, allowing for mocking of the client.
|
||||
|
||||
6
code/__HELPERS/nameof.dm
Normal file
6
code/__HELPERS/nameof.dm
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* NAMEOF: Compile time checked variable name to string conversion
|
||||
* evaluates to a string equal to "X", but compile errors if X isn't a var on datum.
|
||||
* datum may be null, but it does need to be a typed var.
|
||||
**/
|
||||
#define NAMEOF(datum, X) (#X || ##datum.##X)
|
||||
@@ -195,9 +195,9 @@ var/syndicate_code_response//Code response for traitors.
|
||||
if(names.len&&prob(70))
|
||||
code_phrase += pick(names)
|
||||
else
|
||||
code_phrase += pick(pick(first_names_male,first_names_female))
|
||||
code_phrase += pick(pick(GLOB.first_names_male, GLOB.first_names_female))
|
||||
code_phrase += " "
|
||||
code_phrase += pick(last_names)
|
||||
code_phrase += pick(GLOB.last_names)
|
||||
if(2)
|
||||
code_phrase += pick(joblist)//Returns a job.
|
||||
safety -= 1
|
||||
@@ -213,9 +213,9 @@ var/syndicate_code_response//Code response for traitors.
|
||||
if(1)
|
||||
code_phrase += pick(nouns)
|
||||
if(2)
|
||||
code_phrase += pick(adjectives)
|
||||
code_phrase += pick(GLOB.adjectives)
|
||||
if(3)
|
||||
code_phrase += pick(verbs)
|
||||
code_phrase += pick(GLOB.verbs)
|
||||
if(words==1)
|
||||
code_phrase += "."
|
||||
else
|
||||
@@ -246,7 +246,7 @@ var/syndicate_code_response//Code response for traitors.
|
||||
break
|
||||
|
||||
// Update our pda and id if we have them on our person.
|
||||
var/list/searching = GetAllContents()
|
||||
var/list/searching = get_all_contents()
|
||||
var/search_id = 1
|
||||
var/search_pda = 1
|
||||
|
||||
|
||||
6
code/__HELPERS/pass.dm
Normal file
6
code/__HELPERS/pass.dm
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* A do nothing proc.
|
||||
* There's a good reason we have this. I think. I hope.
|
||||
*/
|
||||
/proc/pass(...)
|
||||
return
|
||||
@@ -94,7 +94,9 @@
|
||||
/proc/HeapPathWeightCompare(datum/jps_node/a, datum/jps_node/b)
|
||||
return b.f_value - a.f_value
|
||||
|
||||
/// The datum used to handle the JPS pathfinding, completely self-contained
|
||||
/**
|
||||
* The datum used to handle the JPS pathfinding, completely self-contained.
|
||||
*/
|
||||
/datum/pathfind
|
||||
/// The thing that we're actually trying to path for
|
||||
var/atom/movable/caller
|
||||
@@ -179,7 +181,10 @@
|
||||
qdel(open)
|
||||
return path
|
||||
|
||||
/// Called when we've hit the goal with the node that represents the last tile, then sets the path var to that path so it can be returned by [datum/pathfind/proc/search]
|
||||
/**
|
||||
* Called when we've hit the goal with the node that represents the last tile,
|
||||
* then sets the path var to that path so it can be returned by [datum/pathfind/proc/search]
|
||||
*/
|
||||
/datum/pathfind/proc/unwind_path(datum/jps_node/unwind_node)
|
||||
path = new()
|
||||
var/turf/iter_turf = unwind_node.tile
|
||||
|
||||
@@ -26,7 +26,7 @@ GLOBAL_LIST_INIT(pipe_paint_colors, list(
|
||||
))
|
||||
|
||||
///List that sorts the colors and is used for setting up the pipes layer so that they overlap correctly
|
||||
GLOBAL_LIST_INIT(pipe_colors_ordered, sortTim(list(
|
||||
GLOBAL_LIST_INIT(pipe_colors_ordered, tim_sort(list(
|
||||
PIPE_COLOR_BLUE = -5,
|
||||
PIPE_COLOR_BROWN = -4,
|
||||
PIPE_COLOR_CYAN = -3,
|
||||
@@ -40,7 +40,7 @@ GLOBAL_LIST_INIT(pipe_colors_ordered, sortTim(list(
|
||||
), /proc/cmp_text_asc))
|
||||
|
||||
///Names shown in the examine for every colored atmos component
|
||||
GLOBAL_LIST_INIT(pipe_color_name, sortTim(list(
|
||||
GLOBAL_LIST_INIT(pipe_color_name, tim_sort(list(
|
||||
PIPE_COLOR_GREY = "omni",
|
||||
PIPE_COLOR_BLUE = "blue",
|
||||
PIPE_COLOR_RED = "red",
|
||||
|
||||
15
code/__HELPERS/ref.dm
Normal file
15
code/__HELPERS/ref.dm
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* \ref behaviour got changed in 512 so this is necesary to replicate old behaviour.
|
||||
* If it ever becomes necesary to get a more performant REF(), this lies here in wait
|
||||
* #define REF(thing) (thing && istype(thing, /datum) && (thing:datum_flags & DF_USE_TAG) && thing:tag ? "[thing:tag]" : "\ref[thing]")
|
||||
*/
|
||||
/proc/REF(input)
|
||||
if(istype(input, /datum))
|
||||
var/datum/thing = input
|
||||
if(thing.datum_flags & DF_USE_TAG)
|
||||
if(!thing.tag)
|
||||
stack_trace("A ref was requested of an object with DF_USE_TAG set but no tag: [thing]")
|
||||
thing.datum_flags &= ~DF_USE_TAG
|
||||
else
|
||||
return "\[[url_encode(thing.tag)]\]"
|
||||
return "\ref[input]"
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/standard_reboot()
|
||||
if(ready_for_reboot)
|
||||
if(mode.station_was_nuked)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//Runs the command in the system's shell, returns a list of (error code, stdout, stderr)
|
||||
//! Runs the command in the system's shell, returns a list of (error code, stdout, stderr)
|
||||
|
||||
#define SHELLEO_NAME "data/shelleo."
|
||||
#define SHELLEO_ERR ".err"
|
||||
@@ -34,7 +34,8 @@
|
||||
fdel(err_file)
|
||||
shelleo_ids[shelleo_id] = FALSE
|
||||
else
|
||||
CRASH("Operating System: [world.system_type] not supported") // If you encounter this error, you are encouraged to update this proc with support for the new operating system
|
||||
//? If you encounter this error, you are encouraged to update this proc with support for the new operating system.
|
||||
CRASH("Operating System: [world.system_type] not supported")
|
||||
. = list(errorcode, stdout, stderr)
|
||||
#undef SHELLEO_NAME
|
||||
#undef SHELLEO_ERR
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
//simple insertion sort - generally faster than merge for runs of 7 or smaller
|
||||
/proc/sortInsert(list/L, cmp=/proc/cmp_numeric_asc, associative, fromIndex=1, toIndex=0)
|
||||
/**
|
||||
* InsertionSort
|
||||
* - Generally faster than merge for runs of 7 or smaller.
|
||||
*/
|
||||
/proc/insertion_sort(list/L, cmp=/proc/cmp_numeric_asc, associative, fromIndex=1, toIndex=0)
|
||||
if(L && L.len >= 2)
|
||||
fromIndex = fromIndex % L.len
|
||||
toIndex = toIndex % (L.len+1)
|
||||
@@ -8,12 +11,12 @@
|
||||
if(toIndex <= 0)
|
||||
toIndex += L.len + 1
|
||||
|
||||
var/datum/sortInstance/SI = GLOB.sortInstance
|
||||
var/datum/sort_instance/SI = GLOB.sort_instance
|
||||
if(!SI)
|
||||
SI = new
|
||||
SI.L = L
|
||||
SI.cmp = cmp
|
||||
SI.associative = associative
|
||||
|
||||
SI.binarySort(fromIndex, toIndex, fromIndex)
|
||||
SI.binary_sort(fromIndex, toIndex, fromIndex)
|
||||
return L
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
//merge-sort - gernerally faster than insert sort, for runs of 7 or larger
|
||||
/proc/sortMerge(list/L, cmp=/proc/cmp_numeric_asc, associative, fromIndex=1, toIndex)
|
||||
/**
|
||||
* MergeSort
|
||||
* - Gernerally faster than insert sort, for runs of 7 or larger.
|
||||
*/
|
||||
/proc/merge_sort(list/L, cmp=/proc/cmp_numeric_asc, associative, fromIndex=1, toIndex)
|
||||
if(L && L.len >= 2)
|
||||
fromIndex = fromIndex % L.len
|
||||
toIndex = toIndex % (L.len+1)
|
||||
@@ -8,12 +11,12 @@
|
||||
if(toIndex <= 0)
|
||||
toIndex += L.len + 1
|
||||
|
||||
var/datum/sortInstance/SI = GLOB.sortInstance
|
||||
var/datum/sort_instance/SI = GLOB.sort_instance
|
||||
if(!SI)
|
||||
SI = new
|
||||
SI.L = L
|
||||
SI.cmp = cmp
|
||||
SI.associative = associative
|
||||
|
||||
SI.mergeSort(fromIndex, toIndex)
|
||||
SI.sort_merge(fromIndex, toIndex)
|
||||
return L
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//TimSort interface
|
||||
/proc/sortTim(list/L, cmp=/proc/cmp_numeric_asc, associative, fromIndex=1, toIndex=0)
|
||||
/**
|
||||
* TimSort
|
||||
*/
|
||||
/proc/tim_sort(list/L, cmp=/proc/cmp_numeric_asc, associative, fromIndex=1, toIndex=0)
|
||||
if(L && L.len >= 2)
|
||||
fromIndex = fromIndex % L.len
|
||||
toIndex = toIndex % (L.len+1)
|
||||
@@ -8,7 +10,7 @@
|
||||
if(toIndex <= 0)
|
||||
toIndex += L.len + 1
|
||||
|
||||
var/datum/sortInstance/SI = GLOB.sortInstance
|
||||
var/datum/sort_instance/SI = GLOB.sort_instance
|
||||
if(!SI)
|
||||
SI = new
|
||||
|
||||
@@ -16,5 +18,5 @@
|
||||
SI.cmp = cmp
|
||||
SI.associative = associative
|
||||
|
||||
SI.sortTim(fromIndex, toIndex)
|
||||
SI.tim_sort(fromIndex, toIndex)
|
||||
return L
|
||||
|
||||
@@ -1,108 +1,120 @@
|
||||
//These are macros used to reduce on proc calls
|
||||
#define fetchElement(L, i) (associative) ? L[L[i]] : L[i]
|
||||
/**
|
||||
* These are macros used to reduce on proc calls.
|
||||
*/
|
||||
#define FETCH_ELEMENT(L, i) (associative) ? L[L[i]] : L[i]
|
||||
|
||||
//Minimum sized sequence that will be merged. Anything smaller than this will use binary-insertion sort.
|
||||
//Should be a power of 2
|
||||
/**
|
||||
* Minimum sized sequence that will be merged. Anything smaller than this will use binary-insertion sort.
|
||||
* Should be a power of 2
|
||||
*/
|
||||
#define MIN_MERGE 32
|
||||
|
||||
//When we get into galloping mode, we stay there until both runs win less often than MIN_GALLOP consecutive times.
|
||||
/**
|
||||
* When we get into galloping mode, we stay there until both runs win less often than MIN_GALLOP consecutive times.
|
||||
*/
|
||||
#define MIN_GALLOP 7
|
||||
|
||||
//This is a global instance to allow much of this code to be reused. The interfaces are kept separately
|
||||
GLOBAL_DATUM_INIT(sortInstance, /datum/sortInstance, new())
|
||||
/datum/sortInstance
|
||||
//The array being sorted.
|
||||
/**
|
||||
* This is a global instance to allow much of this code to be reused. The interfaces are kept separately.
|
||||
*/
|
||||
GLOBAL_DATUM_INIT(sort_instance, /datum/sort_instance, new())
|
||||
/datum/sort_instance
|
||||
/// The array being sorted.
|
||||
var/list/L
|
||||
|
||||
//The comparator proc-reference
|
||||
/// The comparator proc-reference.
|
||||
var/cmp = /proc/cmp_numeric_asc
|
||||
|
||||
//whether we are sorting list keys (0: L[i]) or associated values (1: L[L[i]])
|
||||
/// Whether we are sorting list keys (0: L[i]) or associated values (1: L[L[i]])
|
||||
var/associative = 0
|
||||
|
||||
//This controls when we get *into* galloping mode. It is initialized to MIN_GALLOP.
|
||||
//The mergeLo and mergeHi methods nudge it higher for random data, and lower for highly structured data.
|
||||
var/minGallop = MIN_GALLOP
|
||||
/**
|
||||
* This controls when we get *into* galloping mode. It is initialized to MIN_GALLOP.
|
||||
* The merge_low and merge_high methods nudge it higher for random data, and lower for highly structured data.
|
||||
*/
|
||||
var/min_gallop = MIN_GALLOP
|
||||
|
||||
//Stores information regarding runs yet to be merged.
|
||||
//Run i starts at runBase[i] and extends for runLen[i] elements.
|
||||
//runBase[i] + runLen[i] == runBase[i+1]
|
||||
var/list/runBases = list()
|
||||
var/list/runLens = list()
|
||||
/**
|
||||
* Stores information regarding runs yet to be merged.
|
||||
* Run i starts at runBase[i] and extends for runLen[i] elements.
|
||||
* runBase[i] + runLen[i] == runBase[i+1]
|
||||
*/
|
||||
var/list/run_bases = list()
|
||||
var/list/run_lens = list()
|
||||
|
||||
|
||||
/datum/sortInstance/proc/sortTim(start, end)
|
||||
runBases.Cut()
|
||||
runLens.Cut()
|
||||
/datum/sort_instance/proc/tim_sort(start, end)
|
||||
run_bases.Cut()
|
||||
run_lens.Cut()
|
||||
|
||||
var/remaining = end - start
|
||||
|
||||
//If array is small, do a 'mini-TimSort' with no merges
|
||||
// If array is small, do a 'mini-TimSort' with no merges.
|
||||
if(remaining < MIN_MERGE)
|
||||
var/initRunLen = countRunAndMakeAscending(start, end)
|
||||
binarySort(start, end, start+initRunLen)
|
||||
var/initRunLen = count_run_and_make_ascending(start, end)
|
||||
binary_sort(start, end, start+initRunLen)
|
||||
return
|
||||
|
||||
//March over the array finding natural runs
|
||||
//Extend any short natural runs to runs of length minRun
|
||||
var/minRun = minRunLength(remaining)
|
||||
// March over the array finding natural runs.
|
||||
// Extend any short natural runs to runs of length minRun.
|
||||
var/minRun = min_run_length(remaining)
|
||||
|
||||
do
|
||||
//identify next run
|
||||
var/runLen = countRunAndMakeAscending(start, end)
|
||||
// Identify next run.
|
||||
var/runLen = count_run_and_make_ascending(start, end)
|
||||
|
||||
//if run is short, extend to min(minRun, remaining)
|
||||
// If run is short, extend to min(minRun, remaining)
|
||||
if(runLen < minRun)
|
||||
var/force = (remaining <= minRun) ? remaining : minRun
|
||||
|
||||
binarySort(start, start+force, start+runLen)
|
||||
binary_sort(start, start+force, start+runLen)
|
||||
runLen = force
|
||||
|
||||
//add data about run to queue
|
||||
runBases.Add(start)
|
||||
runLens.Add(runLen)
|
||||
// Add data about run to queue.
|
||||
run_bases.Add(start)
|
||||
run_lens.Add(runLen)
|
||||
|
||||
//maybe merge
|
||||
mergeCollapse()
|
||||
// Maybe merge.
|
||||
merge_collapse()
|
||||
|
||||
//Advance to find next run
|
||||
// Advance to find next run.
|
||||
start += runLen
|
||||
remaining -= runLen
|
||||
|
||||
while(remaining > 0)
|
||||
|
||||
|
||||
//Merge all remaining runs to complete sort
|
||||
//ASSERT(start == end)
|
||||
mergeForceCollapse();
|
||||
//ASSERT(runBases.len == 1)
|
||||
// Merge all remaining runs to complete sort.
|
||||
// ASSERT(start == end)
|
||||
merge_force_collapse();
|
||||
// ASSERT(run_bases.len == 1)
|
||||
|
||||
//reset minGallop, for successive calls
|
||||
minGallop = MIN_GALLOP
|
||||
// reset min_gallop, for successive calls
|
||||
min_gallop = MIN_GALLOP
|
||||
|
||||
return L
|
||||
|
||||
/*
|
||||
Sorts the specified portion of the specified array using a binary
|
||||
insertion sort. This is the best method for sorting small numbers
|
||||
of elements. It requires O(n log n) compares, but O(n^2) data
|
||||
movement (worst case).
|
||||
|
||||
If the initial part of the specified range is already sorted,
|
||||
this method can take advantage of it: the method assumes that the
|
||||
elements in range [lo,start) are already sorted
|
||||
|
||||
lo the index of the first element in the range to be sorted
|
||||
hi the index after the last element in the range to be sorted
|
||||
start the index of the first element in the range that is not already known to be sorted
|
||||
/**
|
||||
* Sorts the specified portion of the specified array using a binary
|
||||
* insertion sort. This is the best method for sorting small numbers
|
||||
* of elements. It requires O(n log n) compares, but O(n^2) data
|
||||
* movement (worst case).
|
||||
*
|
||||
* If the initial part of the specified range is already sorted,
|
||||
* this method can take advantage of it: the method assumes that the
|
||||
* elements in range [lo,start) are already sorted
|
||||
*
|
||||
* lo the index of the first element in the range to be sorted
|
||||
* hi the index after the last element in the range to be sorted
|
||||
* start the index of the first element in the range that is not already known to be sorted
|
||||
*/
|
||||
/datum/sortInstance/proc/binarySort(lo, hi, start)
|
||||
/datum/sort_instance/proc/binary_sort(lo, hi, start)
|
||||
//ASSERT(lo <= start && start <= hi)
|
||||
if(start <= lo)
|
||||
start = lo + 1
|
||||
|
||||
for(,start < hi, ++start)
|
||||
var/pivot = fetchElement(L,start)
|
||||
var/pivot = FETCH_ELEMENT(L,start)
|
||||
|
||||
//set left and right to the index where pivot belongs
|
||||
var/left = lo
|
||||
@@ -113,198 +125,215 @@ GLOBAL_DATUM_INIT(sortInstance, /datum/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)(fetchElement(L,mid), pivot) > 0)
|
||||
if(call(cmp)(FETCH_ELEMENT(L,mid), pivot) > 0)
|
||||
right = mid
|
||||
else
|
||||
left = mid+1
|
||||
|
||||
//ASSERT(left == right)
|
||||
moveElement(L, start, left) //move pivot element to correct location in the sorted range
|
||||
move_element(L, start, left) //move pivot element to correct location in the sorted range
|
||||
|
||||
/*
|
||||
Returns the length of the run beginning at the specified position and reverses the run if it is back-to-front
|
||||
|
||||
A run is the longest ascending sequence with:
|
||||
a[lo] <= a[lo + 1] <= a[lo + 2] <= ...
|
||||
or the longest descending sequence with:
|
||||
a[lo] > a[lo + 1] > a[lo + 2] > ...
|
||||
|
||||
For its intended use in a stable mergesort, the strictness of the
|
||||
definition of "descending" is needed so that the call can safely
|
||||
reverse a descending sequence without violating stability.
|
||||
/**
|
||||
* Returns the length of the run beginning at the specified position and reverses the run if it is back-to-front.
|
||||
*
|
||||
* A run is the longest ascending sequence with:
|
||||
* * a[lo] <= a[lo + 1] <= a[lo + 2] <= ...
|
||||
*
|
||||
* or the longest descending sequence with:
|
||||
* * a[lo] > a[lo + 1] > a[lo + 2] > ...
|
||||
*
|
||||
* For its intended use in a stable mergesort, the strictness of the
|
||||
* definition of "descending" is needed so that the call can safely
|
||||
* reverse a descending sequence without violating stability.
|
||||
*/
|
||||
/datum/sortInstance/proc/countRunAndMakeAscending(lo, hi)
|
||||
//ASSERT(lo < hi)
|
||||
/datum/sort_instance/proc/count_run_and_make_ascending(lo, hi)
|
||||
// ASSERT(lo < hi)
|
||||
|
||||
var/runHi = lo + 1
|
||||
if(runHi >= hi)
|
||||
var/run_hi = lo + 1
|
||||
if(run_hi >= hi)
|
||||
return 1
|
||||
|
||||
var/last = fetchElement(L,lo)
|
||||
var/current = fetchElement(L,runHi++)
|
||||
var/last = FETCH_ELEMENT(L,lo)
|
||||
var/current = FETCH_ELEMENT(L,run_hi++)
|
||||
|
||||
if(call(cmp)(current, last) < 0)
|
||||
while(runHi < hi)
|
||||
while(run_hi < hi)
|
||||
last = current
|
||||
current = fetchElement(L,runHi)
|
||||
current = FETCH_ELEMENT(L,run_hi)
|
||||
if(call(cmp)(current, last) >= 0)
|
||||
break
|
||||
++runHi
|
||||
reverseRange(L, lo, runHi)
|
||||
++run_hi
|
||||
reverseRange(L, lo, run_hi)
|
||||
else
|
||||
while(runHi < hi)
|
||||
while(run_hi < hi)
|
||||
last = current
|
||||
current = fetchElement(L,runHi)
|
||||
current = FETCH_ELEMENT(L,run_hi)
|
||||
if(call(cmp)(current, last) < 0)
|
||||
break
|
||||
++runHi
|
||||
++run_hi
|
||||
|
||||
return runHi - lo
|
||||
return run_hi - lo
|
||||
|
||||
//Returns the minimum acceptable run length for an array of the specified length.
|
||||
//Natural runs shorter than this will be extended with binarySort
|
||||
/datum/sortInstance/proc/minRunLength(n)
|
||||
//ASSERT(n >= 0)
|
||||
var/r = 0 //becomes 1 if any bits are shifted off
|
||||
/**
|
||||
* Returns the minimum acceptable run length for an array of the specified length.
|
||||
* Natural runs shorter than this will be extended with binary_sort
|
||||
*/
|
||||
/datum/sort_instance/proc/min_run_length(n)
|
||||
// ASSERT(n >= 0)
|
||||
// Becomes 1 if any bits are shifted off.
|
||||
var/r = 0
|
||||
while(n >= MIN_MERGE)
|
||||
r |= (n & 1)
|
||||
n >>= 1
|
||||
return n + r
|
||||
|
||||
//Examines the stack of runs waiting to be merged and merges adjacent runs until the stack invariants are reestablished:
|
||||
// runLen[i-3] > runLen[i-2] + runLen[i-1]
|
||||
// runLen[i-2] > runLen[i-1]
|
||||
//This method is called each time a new run is pushed onto the stack.
|
||||
//So the invariants are guaranteed to hold for i<stackSize upon entry to the method
|
||||
/datum/sortInstance/proc/mergeCollapse()
|
||||
while(runBases.len >= 2)
|
||||
var/n = runBases.len - 1
|
||||
if(n > 1 && runLens[n-1] <= runLens[n] + runLens[n+1])
|
||||
if(runLens[n-1] < runLens[n+1])
|
||||
/**
|
||||
* Examines the stack of runs waiting to be merged and merges adjacent runs until the stack invariants are reestablished:
|
||||
* * runLen[i-3] > runLen[i-2] + runLen[i-1]
|
||||
* * runLen[i-2] > runLen[i-1]
|
||||
*
|
||||
* This method is called each time a new run is pushed onto the stack.
|
||||
* So the invariants are guaranteed to hold for i<stackSize upon entry to the method.
|
||||
*/
|
||||
/datum/sort_instance/proc/merge_collapse()
|
||||
while(run_bases.len >= 2)
|
||||
var/n = run_bases.len - 1
|
||||
if(n > 1 && run_lens[n - 1] <= run_lens[n] + run_lens[n + 1])
|
||||
if(run_lens[n - 1] < run_lens[n + 1])
|
||||
--n
|
||||
mergeAt(n)
|
||||
else if(runLens[n] <= runLens[n+1])
|
||||
mergeAt(n)
|
||||
merge_at(n)
|
||||
else if(run_lens[n] <= run_lens[n + 1])
|
||||
merge_at(n)
|
||||
else
|
||||
break //Invariant is established
|
||||
// Invariant is established.
|
||||
break
|
||||
|
||||
|
||||
//Merges all runs on the stack until only one remains.
|
||||
//Called only once, to finalise the sort
|
||||
/datum/sortInstance/proc/mergeForceCollapse()
|
||||
while(runBases.len >= 2)
|
||||
var/n = runBases.len - 1
|
||||
if(n > 1 && runLens[n-1] < runLens[n+1])
|
||||
/**
|
||||
* Merges all runs on the stack until only one remains.
|
||||
* Called only once, to finalise the sort
|
||||
*/
|
||||
/datum/sort_instance/proc/merge_force_collapse()
|
||||
while(run_bases.len >= 2)
|
||||
var/n = run_bases.len - 1
|
||||
if(n > 1 && run_lens[n - 1] < run_lens[n + 1])
|
||||
--n
|
||||
mergeAt(n)
|
||||
merge_at(n)
|
||||
|
||||
/**
|
||||
* Merges the two consecutive runs at stack indices i and i+1.
|
||||
* Run i must be the penultimate or antepenultimate run on the stack.
|
||||
* In other words, i must be equal to stackSize-2 or stackSize-3.
|
||||
*/
|
||||
/datum/sort_instance/proc/merge_at(i)
|
||||
// ASSERT(run_bases.len >= 2)
|
||||
// ASSERT(i >= 1)
|
||||
// ASSERT(i == run_bases.len - 1 || i == run_bases.len - 2)
|
||||
|
||||
//Merges the two consecutive runs at stack indices i and i+1
|
||||
//Run i must be the penultimate or antepenultimate run on the stack
|
||||
//In other words, i must be equal to stackSize-2 or stackSize-3
|
||||
/datum/sortInstance/proc/mergeAt(i)
|
||||
//ASSERT(runBases.len >= 2)
|
||||
//ASSERT(i >= 1)
|
||||
//ASSERT(i == runBases.len - 1 || i == runBases.len - 2)
|
||||
var/base1 = run_bases[i]
|
||||
var/base2 = run_bases[i+1]
|
||||
var/len1 = run_lens[i]
|
||||
var/len2 = run_lens[i+1]
|
||||
|
||||
var/base1 = runBases[i]
|
||||
var/base2 = runBases[i+1]
|
||||
var/len1 = runLens[i]
|
||||
var/len2 = runLens[i+1]
|
||||
// ASSERT(len1 > 0 && len2 > 0)
|
||||
// ASSERT(base1 + len1 == base2)
|
||||
|
||||
//ASSERT(len1 > 0 && len2 > 0)
|
||||
//ASSERT(base1 + len1 == base2)
|
||||
/**
|
||||
* Record the legth of the combined runs. If i is the 3rd last run now, also slide over the last run.
|
||||
* (which isn't involved in this merge). The current run (i+1) goes away in any case.
|
||||
*/
|
||||
run_lens[i] += run_lens[i+1]
|
||||
run_lens.Cut(i+1, i+2)
|
||||
run_bases.Cut(i+1, i+2)
|
||||
|
||||
//Record the legth of the combined runs. If i is the 3rd last run now, also slide over the last run
|
||||
//(which isn't involved in this merge). The current run (i+1) goes away in any case.
|
||||
runLens[i] += runLens[i+1]
|
||||
runLens.Cut(i+1, i+2)
|
||||
runBases.Cut(i+1, i+2)
|
||||
|
||||
|
||||
//Find where the first element of run2 goes in run1.
|
||||
//Prior elements in run1 can be ignored (because they're already in place)
|
||||
var/k = gallopRight(fetchElement(L,base2), base1, len1, 0)
|
||||
//ASSERT(k >= 0)
|
||||
/**
|
||||
* Find where the first element of run2 goes in run1.
|
||||
* Prior elements in run1 can be ignored (because they're already in place)
|
||||
*/
|
||||
var/k = gallop_right(FETCH_ELEMENT(L,base2), base1, len1, 0)
|
||||
// ASSERT(k >= 0)
|
||||
base1 += k
|
||||
len1 -= k
|
||||
if(len1 == 0)
|
||||
return
|
||||
|
||||
//Find where the last element of run1 goes in run2.
|
||||
//Subsequent elements in run2 can be ignored (because they're already in place)
|
||||
len2 = gallopLeft(fetchElement(L,base1 + len1 - 1), base2, len2, len2-1)
|
||||
//ASSERT(len2 >= 0)
|
||||
/**
|
||||
* Find where the last element of run1 goes in run2.
|
||||
* Subsequent elements in run2 can be ignored (because they're already in place)
|
||||
*/
|
||||
len2 = gallop_left(FETCH_ELEMENT(L,base1 + len1 - 1), base2, len2, len2-1)
|
||||
// ASSERT(len2 >= 0)
|
||||
if(len2 == 0)
|
||||
return
|
||||
|
||||
//Merge remaining runs, using tmp array with min(len1, len2) elements
|
||||
// Merge remaining runs, using tmp array with min(len1, len2) elements
|
||||
if(len1 <= len2)
|
||||
mergeLo(base1, len1, base2, len2)
|
||||
merge_low(base1, len1, base2, len2)
|
||||
else
|
||||
mergeHi(base1, len1, base2, len2)
|
||||
merge_high(base1, len1, base2, len2)
|
||||
|
||||
|
||||
/*
|
||||
Locates the position to insert key within the specified sorted range
|
||||
If the range contains elements equal to key, this will return the index of the LEFTMOST of those elements
|
||||
|
||||
key the element to be inserted into the sorted range
|
||||
base the index of the first element of the sorted range
|
||||
len the length of the sorted range, must be greater than 0
|
||||
hint the offset from base at which to begin the search, such that 0 <= hint < len; i.e. base <= hint < base+hint
|
||||
|
||||
Returns the index at which to insert element 'key'
|
||||
/**
|
||||
* Locates the position to insert key within the specified sorted range
|
||||
* If the range contains elements equal to key, this will return the index of the LEFTMOST of those elements
|
||||
*
|
||||
* key the element to be inserted into the sorted range
|
||||
* base the index of the first element of the sorted range
|
||||
* len the length of the sorted range, must be greater than 0
|
||||
* hint the offset from base at which to begin the search, such that 0 <= hint < len; i.e. base <= hint < base+hint
|
||||
*
|
||||
* Returns the index at which to insert element 'key'
|
||||
*/
|
||||
/datum/sortInstance/proc/gallopLeft(key, base, len, hint)
|
||||
//ASSERT(len > 0 && hint >= 0 && hint < len)
|
||||
/datum/sort_instance/proc/gallop_left(key, base, len, hint)
|
||||
// ASSERT(len > 0 && hint >= 0 && hint < len)
|
||||
|
||||
var/lastOffset = 0
|
||||
var/last_offset = 0
|
||||
var/offset = 1
|
||||
if(call(cmp)(key, fetchElement(L,base+hint)) > 0)
|
||||
var/maxOffset = len - hint
|
||||
while(offset < maxOffset && call(cmp)(key, fetchElement(L,base+hint+offset)) > 0)
|
||||
lastOffset = offset
|
||||
if(call(cmp)(key, FETCH_ELEMENT(L,base+hint)) > 0)
|
||||
var/max_offset = len - hint
|
||||
while(offset < max_offset && call(cmp)(key, FETCH_ELEMENT(L,base + hint + offset)) > 0)
|
||||
last_offset = offset
|
||||
offset = (offset << 1) + 1
|
||||
|
||||
if(offset > maxOffset)
|
||||
offset = maxOffset
|
||||
if(offset > max_offset)
|
||||
offset = max_offset
|
||||
|
||||
lastOffset += hint
|
||||
last_offset += hint
|
||||
offset += hint
|
||||
|
||||
else
|
||||
var/maxOffset = hint + 1
|
||||
while(offset < maxOffset && call(cmp)(key, fetchElement(L,base+hint-offset)) <= 0)
|
||||
lastOffset = offset
|
||||
var/max_offset = hint + 1
|
||||
while(offset < max_offset && call(cmp)(key, FETCH_ELEMENT(L, base + hint-offset)) <= 0)
|
||||
last_offset = offset
|
||||
offset = (offset << 1) + 1
|
||||
|
||||
if(offset > maxOffset)
|
||||
offset = maxOffset
|
||||
if(offset > max_offset)
|
||||
offset = max_offset
|
||||
|
||||
var/temp = lastOffset
|
||||
lastOffset = hint - offset
|
||||
var/temp = last_offset
|
||||
last_offset = hint - offset
|
||||
offset = hint - temp
|
||||
|
||||
//ASSERT(-1 <= lastOffset && lastOffset < offset && offset <= len)
|
||||
// ASSERT(-1 <= last_offset && last_offset < offset && offset <= len)
|
||||
|
||||
//Now L[base+lastOffset] < key <= L[base+offset], so key belongs somewhere to the right of lastOffset but no farther than
|
||||
//offset. Do a binary search with invariant L[base+lastOffset-1] < key <= L[base+offset]
|
||||
++lastOffset
|
||||
while(lastOffset < offset)
|
||||
var/m = lastOffset + ((offset - lastOffset) >> 1)
|
||||
/**
|
||||
* Now L[base+last_offset] < key <= L[base + offset], so key belongs somewhere to the right of last_offset but no farther than
|
||||
* offset. Do a binary search with invariant L[base+last_offset-1] < key <= L[base+offset]
|
||||
*/
|
||||
++last_offset
|
||||
while(last_offset < offset)
|
||||
var/m = last_offset + ((offset - last_offset) >> 1)
|
||||
|
||||
if(call(cmp)(key, fetchElement(L,base+m)) > 0)
|
||||
lastOffset = m + 1
|
||||
if(call(cmp)(key, FETCH_ELEMENT(L, base + m)) > 0)
|
||||
last_offset = m + 1
|
||||
else
|
||||
offset = m
|
||||
|
||||
//ASSERT(lastOffset == offset)
|
||||
// ASSERT(last_offset == offset)
|
||||
return offset
|
||||
|
||||
/**
|
||||
* Like gallopLeft, except that if the range contains an element equal to
|
||||
* key, gallopRight returns the index after the rightmost equal element.
|
||||
/**
|
||||
* Like gallop_left, except that if the range contains an element equal to
|
||||
* key, gallop_right returns the index after the rightmost equal element.
|
||||
*
|
||||
* @param key the key whose insertion point to search for
|
||||
* @param a the array in which to search
|
||||
@@ -315,85 +344,96 @@ GLOBAL_DATUM_INIT(sortInstance, /datum/sortInstance, new())
|
||||
* @param c the comparator used to order the range, and to search
|
||||
* @return the int k, 0 <= k <= n such that a[b + k - 1] <= key < a[b + k]
|
||||
*/
|
||||
/datum/sortInstance/proc/gallopRight(key, base, len, hint)
|
||||
//ASSERT(len > 0 && hint >= 0 && hint < len)
|
||||
/datum/sort_instance/proc/gallop_right(key, base, len, hint)
|
||||
// ASSERT(len > 0 && hint >= 0 && hint < len)
|
||||
|
||||
var/offset = 1
|
||||
var/lastOffset = 0
|
||||
if(call(cmp)(key, fetchElement(L,base+hint)) < 0) //key <= L[base+hint]
|
||||
var/maxOffset = hint + 1 //therefore we want to insert somewhere in the range [base,base+hint] = [base+,base+(hint+1))
|
||||
while(offset < maxOffset && call(cmp)(key, fetchElement(L,base+hint-offset)) < 0) //we are iterating backwards
|
||||
lastOffset = offset
|
||||
offset = (offset << 1) + 1 //1 3 7 15
|
||||
|
||||
if(offset > maxOffset)
|
||||
offset = maxOffset
|
||||
|
||||
var/temp = lastOffset
|
||||
lastOffset = hint - offset
|
||||
offset = hint - temp
|
||||
|
||||
else //key > L[base+hint]
|
||||
var/maxOffset = len - hint //therefore we want to insert somewhere in the range (base+hint,base+len) = [base+hint+1, base+hint+(len-hint))
|
||||
while(offset < maxOffset && call(cmp)(key, fetchElement(L,base+hint+offset)) >= 0)
|
||||
lastOffset = offset
|
||||
var/last_offset = 0
|
||||
// If key <= L[base+hint]
|
||||
if(call(cmp)(key, FETCH_ELEMENT(L, base + hint)) < 0)
|
||||
// Therefore we want to insert somewhere in the range [base,base+hint] = [base+,base+(hint+1))
|
||||
var/max_offset = hint + 1
|
||||
// We are iterating backwards.
|
||||
while(offset < max_offset && call(cmp)(key, FETCH_ELEMENT(L, base + hint-offset)) < 0)
|
||||
last_offset = offset
|
||||
//1 3 7 15
|
||||
offset = (offset << 1) + 1
|
||||
|
||||
if(offset > maxOffset)
|
||||
offset = maxOffset
|
||||
if(offset > max_offset)
|
||||
offset = max_offset
|
||||
|
||||
lastOffset += hint
|
||||
var/temp = last_offset
|
||||
last_offset = hint - offset
|
||||
offset = hint - temp
|
||||
|
||||
// If key > L[base+hint]
|
||||
else
|
||||
// Therefore we want to insert somewhere in the range (base+hint,base+len) = [base+hint+1, base+hint+(len-hint))
|
||||
var/max_offset = len - hint
|
||||
while(offset < max_offset && call(cmp)(key, FETCH_ELEMENT(L, base + hint + offset)) >= 0)
|
||||
last_offset = offset
|
||||
offset = (offset << 1) + 1
|
||||
|
||||
if(offset > max_offset)
|
||||
offset = max_offset
|
||||
|
||||
last_offset += hint
|
||||
offset += hint
|
||||
|
||||
//ASSERT(-1 <= lastOffset && lastOffset < offset && offset <= len)
|
||||
// ASSERT(-1 <= last_offset && last_offset < offset && offset <= len)
|
||||
|
||||
++lastOffset
|
||||
while(lastOffset < offset)
|
||||
var/m = lastOffset + ((offset - lastOffset) >> 1)
|
||||
++last_offset
|
||||
while(last_offset < offset)
|
||||
var/m = last_offset + ((offset - last_offset) >> 1)
|
||||
|
||||
if(call(cmp)(key, fetchElement(L,base+m)) < 0) //key <= L[base+m]
|
||||
// If key <= L[base+m]
|
||||
if(call(cmp)(key, FETCH_ELEMENT(L,base + m)) < 0)
|
||||
offset = m
|
||||
else //key > L[base+m]
|
||||
lastOffset = m + 1
|
||||
// If key > L[base+m]
|
||||
else
|
||||
last_offset = m + 1
|
||||
|
||||
//ASSERT(lastOffset == offset)
|
||||
// ASSERT(last_offset == offset)
|
||||
|
||||
return offset
|
||||
|
||||
|
||||
//Merges two adjacent runs in-place in a stable fashion.
|
||||
//For performance this method should only be called when len1 <= len2!
|
||||
/datum/sortInstance/proc/mergeLo(base1, len1, base2, len2)
|
||||
//ASSERT(len1 > 0 && len2 > 0 && base1 + len1 == base2)
|
||||
/**
|
||||
* Merges two adjacent runs in-place in a stable fashion.
|
||||
* For performance this method should only be called when len1 <= len2!
|
||||
*/
|
||||
/datum/sort_instance/proc/merge_low(base1, len1, base2, len2)
|
||||
// ASSERT(len1 > 0 && len2 > 0 && base1 + len1 == base2)
|
||||
|
||||
var/cursor1 = base1
|
||||
var/cursor2 = base2
|
||||
|
||||
//degenerate cases
|
||||
// Degenerate cases.
|
||||
if(len2 == 1)
|
||||
moveElement(L, cursor2, cursor1)
|
||||
move_element(L, cursor2, cursor1)
|
||||
return
|
||||
|
||||
if(len1 == 1)
|
||||
moveElement(L, cursor1, cursor2+len2)
|
||||
move_element(L, cursor1, cursor2 + len2)
|
||||
return
|
||||
|
||||
|
||||
//Move first element of second run
|
||||
moveElement(L, cursor2++, cursor1++)
|
||||
// Move first element of second run.
|
||||
move_element(L, cursor2++, cursor1++)
|
||||
--len2
|
||||
|
||||
outer:
|
||||
while(1)
|
||||
var/count1 = 0 //# of times in a row that first run won
|
||||
var/count2 = 0 // " " " " " " second run won
|
||||
|
||||
//do the straightfoward thin until one run starts winning consistently
|
||||
/// # of times in a row that first run won.
|
||||
var/count1 = 0
|
||||
/// # of times in a row that second run won.
|
||||
var/count2 = 0
|
||||
|
||||
// Do the straightfoward thin until one run starts winning consistently.
|
||||
do
|
||||
//ASSERT(len1 > 1 && len2 > 0)
|
||||
if(call(cmp)(fetchElement(L,cursor2), fetchElement(L,cursor1)) < 0)
|
||||
moveElement(L, cursor2++, cursor1++)
|
||||
// ASSERT(len1 > 1 && len2 > 0)
|
||||
if(call(cmp)(FETCH_ELEMENT(L, cursor2), FETCH_ELEMENT(L, cursor1)) < 0)
|
||||
move_element(L, cursor2++, cursor1++)
|
||||
--len2
|
||||
|
||||
++count2
|
||||
@@ -410,15 +450,16 @@ GLOBAL_DATUM_INIT(sortInstance, /datum/sortInstance, new())
|
||||
if(--len1 == 1)
|
||||
break outer
|
||||
|
||||
while((count1 | count2) < minGallop)
|
||||
while((count1 | count2) < min_gallop)
|
||||
|
||||
|
||||
//one run is winning consistently so galloping may provide huge benifits
|
||||
//so try galloping, until such time as the run is no longer consistently winning
|
||||
/**
|
||||
* One run is winning consistently so galloping may provide huge benifits
|
||||
* so try galloping, until such time as the run is no longer consistently winning.
|
||||
*/
|
||||
do
|
||||
//ASSERT(len1 > 1 && len2 > 0)
|
||||
// ASSERT(len1 > 1 && len2 > 0)
|
||||
|
||||
count1 = gallopRight(fetchElement(L,cursor2), cursor1, len1, 0)
|
||||
count1 = gallop_right(FETCH_ELEMENT(L, cursor2), cursor1, len1, 0)
|
||||
if(count1)
|
||||
cursor1 += count1
|
||||
len1 -= count1
|
||||
@@ -426,15 +467,15 @@ GLOBAL_DATUM_INIT(sortInstance, /datum/sortInstance, new())
|
||||
if(len1 <= 1)
|
||||
break outer
|
||||
|
||||
moveElement(L, cursor2, cursor1)
|
||||
move_element(L, cursor2, cursor1)
|
||||
++cursor2
|
||||
++cursor1
|
||||
if(--len2 == 0)
|
||||
break outer
|
||||
|
||||
count2 = gallopLeft(fetchElement(L,cursor1), cursor2, len2, 0)
|
||||
count2 = gallop_left(FETCH_ELEMENT(L, cursor1), cursor2, len2, 0)
|
||||
if(count2)
|
||||
moveRange(L, cursor2, cursor1, count2)
|
||||
move_range(L, cursor2, cursor1, count2)
|
||||
|
||||
cursor2 += count2
|
||||
cursor1 += count2
|
||||
@@ -447,52 +488,56 @@ GLOBAL_DATUM_INIT(sortInstance, /datum/sortInstance, new())
|
||||
if(--len1 == 1)
|
||||
break outer
|
||||
|
||||
--minGallop
|
||||
--min_gallop
|
||||
|
||||
while((count1|count2) > MIN_GALLOP)
|
||||
while((count1 | count2) > MIN_GALLOP)
|
||||
|
||||
if(minGallop < 0)
|
||||
minGallop = 0
|
||||
minGallop += 2; // Penalize for leaving gallop mode
|
||||
if(min_gallop < 0)
|
||||
min_gallop = 0
|
||||
// Penalize for leaving gallop mode.
|
||||
min_gallop += 2;
|
||||
|
||||
|
||||
if(len1 == 1)
|
||||
//ASSERT(len2 > 0)
|
||||
moveElement(L, cursor1, cursor2+len2)
|
||||
// ASSERT(len2 > 0)
|
||||
move_element(L, cursor1, cursor2+len2)
|
||||
|
||||
//else
|
||||
//ASSERT(len2 == 0)
|
||||
//ASSERT(len1 > 1)
|
||||
// else
|
||||
// ASSERT(len2 == 0)
|
||||
// ASSERT(len1 > 1)
|
||||
|
||||
|
||||
/datum/sortInstance/proc/mergeHi(base1, len1, base2, len2)
|
||||
//ASSERT(len1 > 0 && len2 > 0 && base1 + len1 == base2)
|
||||
/datum/sort_instance/proc/merge_high(base1, len1, base2, len2)
|
||||
// ASSERT(len1 > 0 && len2 > 0 && base1 + len1 == base2)
|
||||
|
||||
var/cursor1 = base1 + len1 - 1 //start at end of sublists
|
||||
// Start at end of sublists.
|
||||
var/cursor1 = base1 + len1 - 1
|
||||
var/cursor2 = base2 + len2 - 1
|
||||
|
||||
//degenerate cases
|
||||
// Degenerate cases.
|
||||
if(len2 == 1)
|
||||
moveElement(L, base2, base1)
|
||||
move_element(L, base2, base1)
|
||||
return
|
||||
|
||||
if(len1 == 1)
|
||||
moveElement(L, base1, cursor2+1)
|
||||
move_element(L, base1, cursor2+1)
|
||||
return
|
||||
|
||||
moveElement(L, cursor1--, cursor2-- + 1)
|
||||
move_element(L, cursor1--, cursor2-- + 1)
|
||||
--len1
|
||||
|
||||
outer:
|
||||
while(1)
|
||||
var/count1 = 0 //# of times in a row that first run won
|
||||
var/count2 = 0 // " " " " " " second run won
|
||||
/// # of times in a row that first run won.
|
||||
var/count1 = 0
|
||||
/// # of times in a row that second run won.
|
||||
var/count2 = 0
|
||||
|
||||
//do the straightfoward thing until one run starts winning consistently
|
||||
// Do the straightfoward thing until one run starts winning consistently.
|
||||
do
|
||||
//ASSERT(len1 > 0 && len2 > 1)
|
||||
if(call(cmp)(fetchElement(L,cursor2), fetchElement(L,cursor1)) < 0)
|
||||
moveElement(L, cursor1--, cursor2-- + 1)
|
||||
// ASSERT(len1 > 0 && len2 > 1)
|
||||
if(call(cmp)(FETCH_ELEMENT(L, cursor2), FETCH_ELEMENT(L, cursor1)) < 0)
|
||||
move_element(L, cursor1--, cursor2-- + 1)
|
||||
--len1
|
||||
|
||||
++count1
|
||||
@@ -509,18 +554,21 @@ GLOBAL_DATUM_INIT(sortInstance, /datum/sortInstance, new())
|
||||
|
||||
if(len2 == 1)
|
||||
break outer
|
||||
while((count1 | count2) < minGallop)
|
||||
while((count1 | count2) < min_gallop)
|
||||
|
||||
//one run is winning consistently so galloping may provide huge benifits
|
||||
//so try galloping, until such time as the run is no longer consistently winning
|
||||
/**
|
||||
* One run is winning consistently so galloping may provide huge benifits
|
||||
* so try galloping, until such time as the run is no longer consistently winning.
|
||||
*/
|
||||
do
|
||||
//ASSERT(len1 > 0 && len2 > 1)
|
||||
// ASSERT(len1 > 0 && len2 > 1)
|
||||
|
||||
count1 = len1 - gallopRight(fetchElement(L,cursor2), base1, len1, len1-1) //should cursor1 be base1?
|
||||
count1 = len1 - gallop_right(FETCH_ELEMENT(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
|
||||
// cursor1+1 == cursor2 by definition.
|
||||
move_range(L, cursor1 + 1, cursor2 + 1, count1)
|
||||
|
||||
cursor2 -= count1
|
||||
len1 -= count1
|
||||
@@ -533,7 +581,7 @@ GLOBAL_DATUM_INIT(sortInstance, /datum/sortInstance, new())
|
||||
if(--len2 == 1)
|
||||
break outer
|
||||
|
||||
count2 = len2 - gallopLeft(fetchElement(L,cursor1), cursor1+1, len2, len2-1)
|
||||
count2 = len2 - gallop_left(FETCH_ELEMENT(L, cursor1), cursor1 + 1, len2, len2-1)
|
||||
if(count2)
|
||||
cursor2 -= count2
|
||||
len2 -= count2
|
||||
@@ -541,107 +589,110 @@ GLOBAL_DATUM_INIT(sortInstance, /datum/sortInstance, new())
|
||||
if(len2 <= 1)
|
||||
break outer
|
||||
|
||||
moveElement(L, cursor1--, cursor2-- + 1)
|
||||
move_element(L, cursor1--, cursor2-- + 1)
|
||||
--len1
|
||||
|
||||
if(len1 == 0)
|
||||
break outer
|
||||
|
||||
--minGallop
|
||||
while((count1|count2) > MIN_GALLOP)
|
||||
--min_gallop
|
||||
while((count1 | count2) > MIN_GALLOP)
|
||||
|
||||
if(minGallop < 0)
|
||||
minGallop = 0
|
||||
minGallop += 2 // Penalize for leaving gallop mode
|
||||
if(min_gallop < 0)
|
||||
min_gallop = 0
|
||||
// Penalize for leaving gallop mode.
|
||||
min_gallop += 2
|
||||
|
||||
if(len2 == 1)
|
||||
//ASSERT(len1 > 0)
|
||||
// ASSERT(len1 > 0)
|
||||
|
||||
cursor1 -= len1
|
||||
moveRange(L, cursor1+1, cursor2+1, len1)
|
||||
move_range(L, cursor1 + 1, cursor2 + 1, len1)
|
||||
|
||||
//else
|
||||
//ASSERT(len1 == 0)
|
||||
//ASSERT(len2 > 0)
|
||||
// else
|
||||
// ASSERT(len1 == 0)
|
||||
// ASSERT(len2 > 0)
|
||||
|
||||
|
||||
/datum/sortInstance/proc/mergeSort(start, end)
|
||||
/datum/sort_instance/proc/sort_merge(start, end)
|
||||
var/remaining = end - start
|
||||
|
||||
//If array is small, do an insertion sort
|
||||
// If array is small, do an insertion sort.
|
||||
if(remaining < MIN_MERGE)
|
||||
binarySort(start, end, start/*+initRunLen*/)
|
||||
binary_sort(start, end, start/*+initRunLen*/)
|
||||
return
|
||||
|
||||
var/minRun = minRunLength(remaining)
|
||||
var/minRun = min_run_length(remaining)
|
||||
|
||||
do
|
||||
var/runLen = (remaining <= minRun) ? remaining : minRun
|
||||
|
||||
binarySort(start, start+runLen, start)
|
||||
binary_sort(start, start + runLen, start)
|
||||
|
||||
//add data about run to queue
|
||||
runBases.Add(start)
|
||||
runLens.Add(runLen)
|
||||
// Add data about run to queue.
|
||||
run_bases.Add(start)
|
||||
run_lens.Add(runLen)
|
||||
|
||||
//Advance to find next run
|
||||
// Advance to find next run.
|
||||
start += runLen
|
||||
remaining -= runLen
|
||||
|
||||
while(remaining > 0)
|
||||
|
||||
while(runBases.len >= 2)
|
||||
var/n = runBases.len - 1
|
||||
if(n > 1 && runLens[n-1] <= runLens[n] + runLens[n+1])
|
||||
if(runLens[n-1] < runLens[n+1])
|
||||
while(run_bases.len >= 2)
|
||||
var/n = run_bases.len - 1
|
||||
if(n > 1 && run_lens[n - 1] <= run_lens[n] + run_lens[n + 1])
|
||||
if(run_lens[n - 1] < run_lens[n + 1])
|
||||
--n
|
||||
mergeAt2(n)
|
||||
else if(runLens[n] <= runLens[n+1])
|
||||
mergeAt2(n)
|
||||
merge_at_2(n)
|
||||
else if(run_lens[n] <= run_lens[n + 1])
|
||||
merge_at_2(n)
|
||||
else
|
||||
break //Invariant is established
|
||||
// Invariant is established.
|
||||
break
|
||||
|
||||
while(runBases.len >= 2)
|
||||
var/n = runBases.len - 1
|
||||
if(n > 1 && runLens[n-1] < runLens[n+1])
|
||||
while(run_bases.len >= 2)
|
||||
var/n = run_bases.len - 1
|
||||
if(n > 1 && run_lens[n - 1] < run_lens[n + 1])
|
||||
--n
|
||||
mergeAt2(n)
|
||||
merge_at_2(n)
|
||||
|
||||
return L
|
||||
|
||||
/datum/sortInstance/proc/mergeAt2(i)
|
||||
var/cursor1 = runBases[i]
|
||||
var/cursor2 = runBases[i+1]
|
||||
/datum/sort_instance/proc/merge_at_2(i)
|
||||
var/cursor1 = run_bases[i]
|
||||
var/cursor2 = run_bases[i + 1]
|
||||
|
||||
var/end1 = cursor1+runLens[i]
|
||||
var/end2 = cursor2+runLens[i+1]
|
||||
var/end1 = cursor1 + run_lens[i]
|
||||
var/end2 = cursor2 + run_lens[i + 1]
|
||||
|
||||
var/val1 = fetchElement(L,cursor1)
|
||||
var/val2 = fetchElement(L,cursor2)
|
||||
var/val1 = FETCH_ELEMENT(L, cursor1)
|
||||
var/val2 = FETCH_ELEMENT(L, cursor2)
|
||||
|
||||
while(1)
|
||||
if(call(cmp)(val1,val2) <= 0)
|
||||
if(call(cmp)(val1, val2) <= 0)
|
||||
if(++cursor1 >= end1)
|
||||
break
|
||||
val1 = fetchElement(L,cursor1)
|
||||
val1 = FETCH_ELEMENT(L, cursor1)
|
||||
else
|
||||
moveElement(L,cursor2,cursor1)
|
||||
move_element(L, cursor2, cursor1)
|
||||
|
||||
if(++cursor2 >= end2)
|
||||
break
|
||||
++end1
|
||||
++cursor1
|
||||
|
||||
val2 = fetchElement(L,cursor2)
|
||||
val2 = FETCH_ELEMENT(L, cursor2)
|
||||
|
||||
|
||||
//Record the legth of the combined runs. If i is the 3rd last run now, also slide over the last run
|
||||
//(which isn't involved in this merge). The current run (i+1) goes away in any case.
|
||||
runLens[i] += runLens[i+1]
|
||||
runLens.Cut(i+1, i+2)
|
||||
runBases.Cut(i+1, i+2)
|
||||
/**
|
||||
* Record the legth of the combined runs. If i is the 3rd last run now, also slide over the last run
|
||||
* (which isn't involved in this merge). The current run (i+1) goes away in any case.
|
||||
*/
|
||||
run_lens[i] += run_lens[i + 1]
|
||||
run_lens.Cut(i + 1, i + 2)
|
||||
run_bases.Cut(i + 1, i + 2)
|
||||
|
||||
#undef MIN_GALLOP
|
||||
#undef MIN_MERGE
|
||||
|
||||
#undef fetchElement
|
||||
#undef FETCH_ELEMENT
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//
|
||||
// Comparators for use with /datum/sortInstance (or wherever you want)
|
||||
// They should return negative, zero, or positive numbers for a < b, a == b, and a > b respectively.
|
||||
//
|
||||
/**
|
||||
* Comparators for use with /datum/sort_instance (or wherever you want)
|
||||
* They should return negative, zero, or positive numbers for a < b, a == b, and a > b respectively.
|
||||
*/
|
||||
|
||||
/proc/cmp_numeric_dsc(a,b)
|
||||
return b - a
|
||||
@@ -28,7 +28,9 @@ GLOBAL_VAR_INIT(cmp_field, "name")
|
||||
/proc/cmp_records_dsc(datum/data/record/a, datum/data/record/b)
|
||||
return sorttext(a.fields[GLOB.cmp_field], b.fields[GLOB.cmp_field])
|
||||
|
||||
// Datum cmp with vars is always slower than a specialist cmp proc, use your judgement.
|
||||
/**
|
||||
* Datum cmp with vars is always slower than a specialist cmp proc, use your judgement.
|
||||
*/
|
||||
/proc/cmp_datum_numeric_asc(datum/a, datum/b, variable)
|
||||
return cmp_numeric_asc(a.vars[variable], b.vars[variable])
|
||||
|
||||
@@ -47,15 +49,22 @@ GLOBAL_VAR_INIT(cmp_field, "name")
|
||||
/proc/cmp_ckey_dsc(client/a, client/b)
|
||||
return sorttext(a.ckey, b.ckey)
|
||||
|
||||
// Sorts subsystems alphabetically
|
||||
/**
|
||||
* Sorts subsystems alphabetically.
|
||||
*/
|
||||
/proc/cmp_subsystem_display(datum/controller/subsystem/a, datum/controller/subsystem/b)
|
||||
return sorttext(b.name, a.name)
|
||||
|
||||
// Sorts subsystems by init_order
|
||||
/**
|
||||
* Sorts subsystems by init_order.
|
||||
*/
|
||||
/proc/cmp_subsystem_init(datum/controller/subsystem/a, datum/controller/subsystem/b)
|
||||
return initial(b.init_order) - initial(a.init_order) //uses initial() so it can be used on types
|
||||
// Uses initial() so it can be used on types.
|
||||
return initial(b.init_order) - initial(a.init_order)
|
||||
|
||||
// Sorts subsystems by priority
|
||||
/**
|
||||
* Sorts subsystems by priority.
|
||||
*/
|
||||
/proc/cmp_subsystem_priority(datum/controller/subsystem/a, datum/controller/subsystem/b)
|
||||
return a.priority - b.priority
|
||||
|
||||
@@ -65,7 +74,9 @@ GLOBAL_VAR_INIT(cmp_field, "name")
|
||||
/proc/cmp_timer(datum/timedevent/a, datum/timedevent/b)
|
||||
return a.timeToRun - b.timeToRun
|
||||
|
||||
// Sorts qdel statistics recorsd by time and count
|
||||
/**
|
||||
* Sorts qdel statistics recorsd by time and count.
|
||||
*/
|
||||
/proc/cmp_qdel_item_time(datum/qdel_item/A, datum/qdel_item/B)
|
||||
. = B.hard_delete_time - A.hard_delete_time
|
||||
if (!.)
|
||||
@@ -75,32 +86,43 @@ GLOBAL_VAR_INIT(cmp_field, "name")
|
||||
if (!.)
|
||||
. = B.qdels - A.qdels
|
||||
|
||||
// Sorts jobs by department, and then by flag within department
|
||||
/proc/cmp_job_datums(var/datum/job/a, var/datum/job/b)
|
||||
/**
|
||||
* Sorts jobs by department, and then by flag within department.
|
||||
*/
|
||||
/proc/cmp_job_datums(datum/job/a, datum/job/b)
|
||||
. = 0
|
||||
if( LAZYLEN(a.departments) && LAZYLEN(b.departments) )
|
||||
var/list/common_departments = a.departments & b.departments // Makes a list that contains only departments that were in both.
|
||||
// Makes a list that contains only departments that were in both.
|
||||
var/list/common_departments = a.departments & b.departments
|
||||
if(!common_departments.len)
|
||||
. = sorttext(b.departments[1], a.departments[1])
|
||||
|
||||
if(. == 0) //Same department, push up if they're a head
|
||||
// Same department, push up if they're a head.
|
||||
if(. == 0)
|
||||
. = b.sorting_order - a.sorting_order
|
||||
|
||||
if(. == 0) //Already in same sorting order, sort by name
|
||||
// Already in same sorting order, sort by name.
|
||||
if(. == 0)
|
||||
. = sorttext(b.title, a.title)
|
||||
|
||||
/proc/cmp_department_datums(var/datum/department/a, var/datum/department/b)
|
||||
. = b.sorting_order - a.sorting_order // First, sort by the sorting order vars.
|
||||
if(. == 0) // If they have the same var, then sort by name.
|
||||
/proc/cmp_department_datums(datum/department/a, datum/department/b)
|
||||
// First, sort by the sorting order vars.
|
||||
. = b.sorting_order - a.sorting_order
|
||||
// If they have the same var, then sort by name.
|
||||
if(. == 0)
|
||||
. = sorttext(b.name, a.name)
|
||||
|
||||
// Sorts entries in a performance stats list.
|
||||
/**
|
||||
* Sorts entries in a performance stats list.
|
||||
*/
|
||||
/proc/cmp_generic_stat_item_time(list/A, list/B)
|
||||
. = B[STAT_ENTRY_TIME] - A[STAT_ENTRY_TIME]
|
||||
if (!.)
|
||||
. = B[STAT_ENTRY_COUNT] - A[STAT_ENTRY_COUNT]
|
||||
|
||||
// Compares complexity of recipes for use in cooking, etc. This is for telling which recipe to make, not for showing things to the player.
|
||||
/**
|
||||
* Compares complexity of recipes for use in cooking, etc.
|
||||
* This is for telling which recipe to make, not for showing things to the player.
|
||||
*/
|
||||
/proc/cmp_recipe_complexity_dsc(datum/recipe/A, datum/recipe/B)
|
||||
var/a_score = LAZYLEN(A.items) + LAZYLEN(A.reagents) + LAZYLEN(A.fruit)
|
||||
var/b_score = LAZYLEN(B.items) + LAZYLEN(B.reagents) + LAZYLEN(B.fruit)
|
||||
@@ -146,7 +168,8 @@ GLOBAL_VAR_INIT(cmp_field, "name")
|
||||
/datum/proc/compare_to(datum/D)
|
||||
return cmp_text_asc("[src]", "[D]")
|
||||
|
||||
// profile stuff
|
||||
|
||||
//! PROFILE STUFF
|
||||
|
||||
/proc/cmp_profile_avg_time_dsc(list/A, list/B)
|
||||
return (B[PROFILE_ITEM_TIME]/(B[PROFILE_ITEM_COUNT] || 1)) - (A[PROFILE_ITEM_TIME]/(A[PROFILE_ITEM_COUNT] || 1))
|
||||
@@ -156,4 +179,3 @@ GLOBAL_VAR_INIT(cmp_field, "name")
|
||||
|
||||
/proc/cmp_profile_count_dsc(list/A, list/B)
|
||||
return B[PROFILE_ITEM_COUNT] - A[PROFILE_ITEM_COUNT]
|
||||
|
||||
|
||||
14
code/__HELPERS/stack_trace.dm
Normal file
14
code/__HELPERS/stack_trace.dm
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
*? Gives us the stack trace from CRASH() without ending the current proc.
|
||||
*! Do not call directly, use the [stack_trace] macro instead.
|
||||
*/
|
||||
/proc/_stack_trace(message, file, line)
|
||||
CRASH("[message] ([file]:[line])")
|
||||
|
||||
GLOBAL_REAL_VAR(list/stack_trace_storage)
|
||||
/proc/gib_stack_trace()
|
||||
stack_trace_storage = list()
|
||||
stack_trace("")
|
||||
stack_trace_storage.Cut(1, min(3,stack_trace_storage.len))
|
||||
. = stack_trace_storage
|
||||
stack_trace_storage = null
|
||||
28
code/__HELPERS/stoplag.dm
Normal file
28
code/__HELPERS/stoplag.dm
Normal file
@@ -0,0 +1,28 @@
|
||||
//Key thing that stops lag. Cornerstone of performance in ss13, Just sitting here, in unsorted.dm. Now with dedicated file!
|
||||
|
||||
/**
|
||||
* Increases delay as the server gets more overloaded,
|
||||
* as sleeps aren't cheap and sleeping only to wake up and sleep again is wasteful
|
||||
*/
|
||||
#define DELTA_CALC max(((max(TICK_USAGE, world.cpu) / 100) * max(Master.sleep_delta - 1, 1)), 1)
|
||||
|
||||
/**
|
||||
* Returns the number of ticks slept.
|
||||
*/
|
||||
/proc/stoplag(initial_delay)
|
||||
if (!Master || !(Master.current_runlevel & RUNLEVELS_DEFAULT))
|
||||
sleep(world.tick_lag)
|
||||
return 1
|
||||
if (!initial_delay)
|
||||
initial_delay = world.tick_lag
|
||||
. = 0
|
||||
var/i = DS2TICKS(initial_delay)
|
||||
do
|
||||
. += CEILING(i*DELTA_CALC, 1)
|
||||
sleep(i*world.tick_lag*DELTA_CALC)
|
||||
i *= 2
|
||||
while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit))
|
||||
|
||||
#undef DELTA_CALC
|
||||
|
||||
#define UNTIL(X) while(!(X)) stoplag()
|
||||
@@ -1,4 +1,4 @@
|
||||
/proc/create_objects_in_loc(var/atom/loc, var/list/item_paths)
|
||||
/proc/create_objects_in_loc(atom/loc, list/item_paths)
|
||||
if(!istype(loc))
|
||||
CRASH("Inappropriate loction given.")
|
||||
if(!istype(item_paths))
|
||||
|
||||
@@ -56,7 +56,9 @@
|
||||
/proc/sanitize_filename(t)
|
||||
return sanitize_simple_tg(t, list("\n"="", "\t"="", "/"="", "\\"="", "?"="", "%"="", "*"="", ":"="", "|"="", "\""="", "<"="", ">"=""))
|
||||
|
||||
/// Removes a few problematic characters. Renamed because namespace.
|
||||
/**
|
||||
* Removes a few problematic characters. Renamed because namespace.
|
||||
*/
|
||||
/proc/sanitize_simple_tg(t, list/repl_chars = list("\n"="#","\t"="#"))
|
||||
for(var/char in repl_chars)
|
||||
var/index = findtext(t, char)
|
||||
@@ -74,7 +76,9 @@
|
||||
/proc/sanitizeSafe(input, max_length = MAX_MESSAGE_LEN, encode = TRUE, trim = TRUE, extra = TRUE)
|
||||
return sanitize(replace_characters(input, list(">"=" ","<"=" ", "\""="'")), max_length, encode, trim, extra)
|
||||
|
||||
/// Filters out undesirable characters from names.
|
||||
/**
|
||||
* Filters out undesirable characters from names.
|
||||
*/
|
||||
/proc/sanitizeName(input, max_length = MAX_NAME_LEN)
|
||||
if(!input || length(input) > max_length)
|
||||
return //Rejects the input if it is null or if it is longer then the max length allowed
|
||||
@@ -144,7 +148,9 @@
|
||||
|
||||
return output
|
||||
|
||||
/// Returns null if there is any bad text in the string.
|
||||
/**
|
||||
* Returns null if there is any bad text in the string.
|
||||
*/
|
||||
/proc/reject_bad_text(text, max_length = 512, ascii_only = TRUE)
|
||||
var/char_count = 0
|
||||
var/non_whitespace = FALSE
|
||||
@@ -181,7 +187,9 @@
|
||||
else
|
||||
return trim(html_encode(name), max_length) //trim is "outside" because html_encode can expand single symbols into multiple symbols (such as turning < into <)
|
||||
|
||||
/// Used to get a properly sanitized multiline input, of max_length
|
||||
/**
|
||||
* Used to get a properly sanitized multiline input, of max_length.
|
||||
*/
|
||||
/proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length = MAX_MESSAGE_LEN, no_trim = FALSE)
|
||||
var/name = input(user, message, title, default) as message|null
|
||||
if(isnull(name)) // Return null if canceled.
|
||||
@@ -191,7 +199,9 @@
|
||||
else
|
||||
return trim(html_encode(name), max_length)
|
||||
|
||||
/// Old variant. Haven't dared to replace in some places.
|
||||
/**
|
||||
* Old variant. Haven't dared to replace in some places.
|
||||
*/
|
||||
/proc/sanitize_old(var/t,var/list/repl_chars = list("\n"="#","\t"="#"))
|
||||
return html_encode(replace_characters(t,repl_chars))
|
||||
|
||||
@@ -244,77 +254,93 @@
|
||||
t = replacetext(t, char, repl_chars[char])
|
||||
return t
|
||||
|
||||
/// Adds 'u' number of zeros ahead of the text 't'.
|
||||
/**
|
||||
* Adds 'u' number of zeros ahead of the text 't'.
|
||||
*/
|
||||
/proc/add_zero(t, u)
|
||||
while (length(t) < u)
|
||||
t = "0[t]"
|
||||
return t
|
||||
|
||||
/// Adds 'u' number of spaces ahead of the text 't'.
|
||||
/**
|
||||
* Adds 'u' number of spaces ahead of the text 't'.
|
||||
*/
|
||||
/proc/add_lspace(t, u)
|
||||
while(length(t) < u)
|
||||
t = " [t]"
|
||||
return t
|
||||
|
||||
/// Adds 'u' number of spaces behind the text 't'.
|
||||
/**
|
||||
* Adds 'u' number of spaces behind the text 't'.
|
||||
*/
|
||||
/proc/add_tspace(t, u)
|
||||
while(length(t) < u)
|
||||
t = "[t] "
|
||||
return t
|
||||
|
||||
/// Returns a string with reserved characters and spaces before the first letter removed.
|
||||
/**
|
||||
* Returns a string with reserved characters and spaces before the first letter removed.
|
||||
*/
|
||||
/proc/trim_left(text)
|
||||
for (var/i = 1 to length(text))
|
||||
if (text2ascii(text, i) > 32)
|
||||
return copytext(text, i)
|
||||
return ""
|
||||
|
||||
/// Returns a string with reserved characters and spaces after the last letter removed.
|
||||
/**
|
||||
* Returns a string with reserved characters and spaces after the last letter removed.
|
||||
*/
|
||||
/proc/trim_right(text)
|
||||
for (var/i = length(text), i > 0, i--)
|
||||
if (text2ascii(text, i) > 32)
|
||||
return copytext(text, 1, i + 1)
|
||||
return ""
|
||||
|
||||
/// Returns a string with reserved characters and spaces before the first word and after the last word removed.
|
||||
/**
|
||||
* Returns a string with reserved characters and spaces before the first word and after the last word removed.
|
||||
*/
|
||||
/proc/trim(text)
|
||||
return trim_left(trim_right(text))
|
||||
|
||||
/// Returns a string with the first element of the string capitalized.
|
||||
/**
|
||||
* Returns a string with the first element of the string capitalized.
|
||||
*/
|
||||
/proc/capitalize(t as text)
|
||||
return uppertext(copytext(t, 1, 2)) + copytext(t, 2)
|
||||
|
||||
/// syntax is "stringtoreplace"="stringtoreplacewith".
|
||||
/**
|
||||
* Syntax is "stringtoreplace"="stringtoreplacewith".
|
||||
*/
|
||||
/proc/autocorrect(input as text)
|
||||
return input = replace_characters(input, list(
|
||||
" i "=" I ",
|
||||
"i'm"="I'm",
|
||||
"s's"="s'",
|
||||
"isnt"="isn't",
|
||||
"dont"="don't",
|
||||
"shouldnt"="shouldn't",
|
||||
" ive "=" I've ",
|
||||
"whove"="who've",
|
||||
"whod"="who’d",
|
||||
"whats"="what’s",
|
||||
"whatd"="what’d",
|
||||
"thats"="that’s",
|
||||
"thatll"="that’ll",
|
||||
"thatd"="that’d",
|
||||
" nows "=" now’s ",
|
||||
"isnt"="isn’t",
|
||||
" arent "=" aren’t ",
|
||||
"wasnt"="wasn’t",
|
||||
"werent"="weren’t",
|
||||
"havent"="haven’t",
|
||||
"hasnt"="hasn’t",
|
||||
"hadnt"="hadn’t",
|
||||
"doesnt"="doesn’t",
|
||||
"didnt"="didn’t",
|
||||
"couldnt"="couldn’t",
|
||||
"wouldnt"="wouldn’t",
|
||||
"mustnt"="mustn’t",
|
||||
"shouldnt"="shouldn’t",
|
||||
" i " = " I ",
|
||||
"i'm" = "I'm",
|
||||
"s's" = "s'",
|
||||
"isnt" = "isn't",
|
||||
"dont" = "don't",
|
||||
"shouldnt" = "shouldn't",
|
||||
" ive " = " I've ",
|
||||
"whove" = "who've",
|
||||
"whod" = "who’d",
|
||||
"whats" = "what’s",
|
||||
"whatd" = "what’d",
|
||||
"thats" = "that’s",
|
||||
"thatll" = "that’ll",
|
||||
"thatd" = "that’d",
|
||||
" nows " = " now’s ",
|
||||
"isnt" = "isn’t",
|
||||
" arent " = " aren’t ",
|
||||
"wasnt" = "wasn’t",
|
||||
"werent" = "weren’t",
|
||||
"havent" = "haven’t",
|
||||
"hasnt" = "hasn’t",
|
||||
"hadnt" = "hadn’t",
|
||||
"doesnt" = "doesn’t",
|
||||
"didnt" = "didn’t",
|
||||
"couldnt" = "couldn’t",
|
||||
"wouldnt" = "wouldn’t",
|
||||
"mustnt" = "mustn’t",
|
||||
"shouldnt" = "shouldn’t",
|
||||
))
|
||||
|
||||
/**
|
||||
@@ -408,7 +434,9 @@
|
||||
else
|
||||
return "[copytext_preserve_html(string, 1, 37)]..."
|
||||
|
||||
/// Alternative copytext() for encoded text, doesn't break html entities (" and other)
|
||||
/**
|
||||
* Alternative copytext() for encoded text, doesn't break html entities (" and other)
|
||||
*/
|
||||
/proc/copytext_preserve_html(text, first, last)
|
||||
return html_encode(copytext(html_decode(text), first, last))
|
||||
|
||||
@@ -489,7 +517,9 @@ GLOBAL_VAR_INIT(text_tag_icons, new /icon('./icons/chattags.dmi'))
|
||||
t = replacetext(t, "\[editorbr\]", "")
|
||||
return t
|
||||
|
||||
/// Random password generator. // Could've just flustered a bottom.
|
||||
/**
|
||||
* Random password generator. // Could've just flustered a bottom.
|
||||
*/
|
||||
/proc/GenerateKey()
|
||||
// Feel free to move to Helpers.
|
||||
var/newKey
|
||||
@@ -498,7 +528,9 @@ GLOBAL_VAR_INIT(text_tag_icons, new /icon('./icons/chattags.dmi'))
|
||||
newKey += pick("1", "2", "3", "4", "5", "6", "7", "8", "9", "0")
|
||||
return newKey
|
||||
|
||||
/// Used for applying byonds text macros to strings that are loaded at runtime.
|
||||
/**
|
||||
* Used for applying byonds text macros to strings that are loaded at runtime.
|
||||
*/
|
||||
/proc/apply_text_macros(string)
|
||||
var/next_backslash = findtext(string, "\\")
|
||||
if(!next_backslash)
|
||||
@@ -577,7 +609,9 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
|
||||
/proc/random_color()
|
||||
return random_string(6, GLOB.hex_characters)
|
||||
|
||||
/// Readds quotes and apostrophes to HTML-encoded strings.
|
||||
/**
|
||||
* Readds quotes and apostrophes to HTML-encoded strings.
|
||||
*/
|
||||
/proc/readd_quotes(t)
|
||||
var/list/repl_chars = list(""" = "\"","'" = "'")
|
||||
for(var/char in repl_chars)
|
||||
@@ -587,21 +621,27 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
|
||||
index = findtext(t, char)
|
||||
return t
|
||||
|
||||
/// Adds 'char' ahead of 'text' until there are 'count' characters total.
|
||||
/**
|
||||
* Adds 'char' ahead of 'text' until there are 'count' characters total.
|
||||
*/
|
||||
/proc/add_leading(text, count, char = " ")
|
||||
text = "[text]"
|
||||
var/charcount = count - length_char(text)
|
||||
var/list/chars_to_add[max(charcount + 1, 0)]
|
||||
return jointext(chars_to_add, char) + text
|
||||
|
||||
/// Adds 'char' behind 'text' until there are 'count' characters total.
|
||||
/**
|
||||
* Adds 'char' behind 'text' until there are 'count' characters total.
|
||||
*/
|
||||
/proc/add_trailing(text, count, char = " ")
|
||||
text = "[text]"
|
||||
var/charcount = count - length_char(text)
|
||||
var/list/chars_to_add[max(charcount + 1, 0)]
|
||||
return text + jointext(chars_to_add, char)
|
||||
|
||||
/// Removes all non-alphanumerics from the text, keep in mind this can lead to id conflicts.
|
||||
/**
|
||||
* Removes all non-alphanumerics from the text, keep in mind this can lead to id conflicts.
|
||||
*/
|
||||
/proc/sanitize_css_class_name(name)
|
||||
var/static/regex/regex = new(@"[^a-zA-Z0-9]","g")
|
||||
return replacetext(name, regex, "")
|
||||
|
||||
@@ -65,15 +65,22 @@
|
||||
|
||||
/proc/stutter(n)
|
||||
var/te = html_decode(n)
|
||||
var/t = ""//placed before the message. Not really sure what it's for.
|
||||
n = length(n)//length of the entire word
|
||||
/// Placed before the message. Not really sure what it's for.
|
||||
var/t = ""
|
||||
/// Length of the entire word.
|
||||
n = length(n)
|
||||
var/p = null
|
||||
p = 1//1 is the start of any word
|
||||
while(p <= n)//while P, which starts at 1 is less or equal to N which is the length.
|
||||
var/n_letter = copytext(te, p, p + 1)//copies text from a certain distance. In this case, only one letter at a time.
|
||||
/// 1 is the start of any word.
|
||||
p = 1
|
||||
|
||||
// While P, which starts at 1 is less or equal to N which is the length.
|
||||
while(p <= n)
|
||||
// Copies text from a certain distance. In this case, only one letter at a time.
|
||||
var/n_letter = copytext(te, p, p + 1)
|
||||
if (prob(80) && (ckey(n_letter) in list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z")))
|
||||
if (prob(10))
|
||||
n_letter = text("[n_letter]-[n_letter]-[n_letter]-[n_letter]")//replaces the current letter with this instead.
|
||||
// Replaces the current letter with this instead.
|
||||
n_letter = text("[n_letter]-[n_letter]-[n_letter]-[n_letter]")
|
||||
else
|
||||
if (prob(20))
|
||||
n_letter = text("[n_letter]-[n_letter]-[n_letter]")
|
||||
@@ -82,8 +89,10 @@
|
||||
n_letter = null
|
||||
else
|
||||
n_letter = text("[n_letter]-[n_letter]")
|
||||
t = text("[t][n_letter]")//since the above is ran through for each letter, the text just adds up back to the original word.
|
||||
p++//for each letter p is increased to find where the next letter will be.
|
||||
// Since the above is ran through for each letter, the text just adds up back to the original word.
|
||||
t = text("[t][n_letter]")
|
||||
/// For each letter p is increased to find where the next letter will be.
|
||||
p++
|
||||
return sanitize(t)
|
||||
|
||||
/**
|
||||
|
||||
@@ -71,11 +71,15 @@ GLOBAL_VAR_INIT(roundstart_hour, pick(2,7,12,17))
|
||||
/proc/station_time_timestamp(format = "hh:mm:ss", time=world.time)
|
||||
return time2text(station_time(time, TRUE), format)
|
||||
|
||||
/// Returns 1 if it is the selected month and day.
|
||||
/**
|
||||
* Returns 1 if it is the selected month and day.
|
||||
*/
|
||||
/proc/isDay(var/month, var/day)
|
||||
if(isnum(month) && isnum(day))
|
||||
var/MM = text2num(time2text(world.timeofday, "MM")) // Get the current month.
|
||||
var/DD = text2num(time2text(world.timeofday, "DD")) // Get the current day.
|
||||
if(isnum(month) && isnum(day))\
|
||||
/// Get the current month.
|
||||
var/MM = text2num(time2text(world.timeofday, "MM"))
|
||||
/// Get the current day.
|
||||
var/DD = text2num(time2text(world.timeofday, "DD"))
|
||||
if(month == MM && day == DD)
|
||||
return TRUE
|
||||
|
||||
@@ -92,8 +96,10 @@ GLOBAL_VAR_INIT(roundstart_hour, pick(2,7,12,17))
|
||||
if(last_round_duration && world.time < next_duration_update)
|
||||
return last_round_duration
|
||||
|
||||
var/mills = round_duration_in_ds // 1/10 of a second, not real milliseconds but whatever
|
||||
//var/secs = ((mills % 36000) % 600) / 10 //Not really needed, but I'll leave it here for refrence.. or something
|
||||
/// 1/10 of a second, not real milliseconds but whatever.
|
||||
var/mills = round_duration_in_ds
|
||||
/// Not really needed, but I'll leave it here for refrence.. or something.
|
||||
//var/secs = ((mills % 36000) % 600) / 10
|
||||
var/mins = round((mills % 36000) / 600)
|
||||
var/hours = round(mills / 36000)
|
||||
|
||||
@@ -113,7 +119,8 @@ GLOBAL_VAR_INIT(roundstart_hour, pick(2,7,12,17))
|
||||
return midnight_rollovers
|
||||
|
||||
/proc/weekdayofthemonth()
|
||||
var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day
|
||||
/// Get the current day.
|
||||
var/DD = text2num(time2text(world.timeofday, "DD"))
|
||||
switch(DD)
|
||||
if(8 to 13)
|
||||
return 2
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Returns the atom sitting on the turf.
|
||||
// For example, using this on a disk, which is in a bag, on a mob, will return the mob because it's on the turf.
|
||||
/proc/get_atom_on_turf(var/atom/movable/M)
|
||||
var/atom/mloc = M
|
||||
/**
|
||||
* Returns the atom sitting on the turf.
|
||||
* For example, using this on a disk, which is in a bag, on a mob, will return the mob because it's on the turf.
|
||||
*/
|
||||
/proc/get_atom_on_turf(atom/movable/AM)
|
||||
var/atom/mloc = AM
|
||||
while(mloc && mloc.loc && !istype(mloc.loc, /turf/))
|
||||
mloc = mloc.loc
|
||||
return mloc
|
||||
@@ -18,9 +20,11 @@
|
||||
return 0
|
||||
return 1
|
||||
|
||||
// Picks a turf without a mob from the given list of turfs, if one exists.
|
||||
// If no such turf exists, picks any random turf from the given list of turfs.
|
||||
/proc/pick_mobless_turf_if_exists(var/list/start_turfs)
|
||||
/**
|
||||
* Picks a turf without a mob from the given list of turfs, if one exists.
|
||||
* If no such turf exists, picks any random turf from the given list of turfs.
|
||||
*/
|
||||
/proc/pick_mobless_turf_if_exists(list/start_turfs)
|
||||
if(!start_turfs.len)
|
||||
return null
|
||||
|
||||
@@ -33,8 +37,10 @@
|
||||
available_turfs = start_turfs
|
||||
return pick(available_turfs)
|
||||
|
||||
// Picks a turf that is clearance tiles away from the map edge given by dir, on z-level Z
|
||||
/proc/pick_random_edge_turf(var/dir, var/Z, var/clearance = TRANSITIONEDGE + 1)
|
||||
/**
|
||||
* Picks a turf that is clearance tiles away from the map edge given by dir, on z-level Z.
|
||||
*/
|
||||
/proc/pick_random_edge_turf(dir, Z, clearance = TRANSITIONEDGE + 1)
|
||||
if(!dir)
|
||||
return
|
||||
switch(dir)
|
||||
@@ -47,18 +53,20 @@
|
||||
if(WEST)
|
||||
return locate(clearance, rand(clearance, world.maxy - clearance), Z)
|
||||
|
||||
/proc/is_below_sound_pressure(var/turf/T)
|
||||
/proc/is_below_sound_pressure(turf/T)
|
||||
var/pressure = T.return_pressure() || 0
|
||||
if(pressure < SOUND_MINIMUM_PRESSURE)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/*
|
||||
Turf Manipulation
|
||||
*/
|
||||
/**
|
||||
*! Turf Manipulation
|
||||
*/
|
||||
|
||||
// Returns an assoc list that describes how turfs would be changed if the
|
||||
// Turfs in turfs_src were translated by shifting the src_origin to the dst_origin
|
||||
/**
|
||||
* Returns an assoc list that describes how turfs would be changed if the
|
||||
* Turfs in turfs_src were translated by shifting the src_origin to the dst_origin
|
||||
*/
|
||||
/proc/get_turf_translation(turf/src_origin, turf/dst_origin, list/turfs_src)
|
||||
var/list/turf_map = list()
|
||||
for(var/turf/source in turfs_src)
|
||||
@@ -69,11 +77,12 @@
|
||||
var/turf/target = locate(dst_origin.x + x_pos, dst_origin.y + y_pos, dst_origin.z + z_pos)
|
||||
if(!target)
|
||||
log_debug("Null turf in translation @ ([dst_origin.x + x_pos], [dst_origin.y + y_pos], [dst_origin.z + z_pos])")
|
||||
turf_map[source] = target // If target is null, preserve that information in the turf map
|
||||
// If target is null, preserve that information in the turf map.
|
||||
turf_map[source] = target
|
||||
|
||||
return turf_map
|
||||
|
||||
/proc/translate_turfs(var/list/translation, var/area/base_area = null, var/turf/base_turf)
|
||||
/proc/translate_turfs(list/translation, area/base_area = null, turf/base_turf)
|
||||
for(var/turf/source in translation)
|
||||
|
||||
var/turf/target = translation[source]
|
||||
@@ -87,80 +96,84 @@
|
||||
ChangeArea(source, base_area)
|
||||
|
||||
// Change the old turfs (Currently done by translate_turf for us)
|
||||
//for(var/turf/source in translation)
|
||||
// for(var/turf/source in translation)
|
||||
// source.ChangeTurf(base_turf ? base_turf : get_base_turf_by_area(source), 1, 1)
|
||||
|
||||
// Parmaters for stupid historical reasons are:
|
||||
// T - Origin
|
||||
// B - Destination
|
||||
/proc/translate_turf(var/turf/T, var/turf/B, var/turftoleave = null)
|
||||
/proc/translate_turf(turf/Origin, turf/Destination, turftoleave = null)
|
||||
|
||||
// You can stay, though.
|
||||
if(istype(T,/turf/space))
|
||||
log_debug("Tried to translate a space turf: src=[log_info_line(T)] dst=[log_info_line(B)]")
|
||||
if (istype(Origin, /turf/space))
|
||||
log_debug("Tried to translate a space turf: src=[log_info_line(Origin)] dst=[log_info_line(Destination)]")
|
||||
return FALSE // TODO - Is this really okay to do nothing?
|
||||
|
||||
var/turf/X // New Destination Turf
|
||||
|
||||
var/old_dir1 = T.dir
|
||||
var/old_icon_state1 = T.icon_state
|
||||
var/old_icon1 = T.icon
|
||||
var/old_underlays = T.underlays.Copy()
|
||||
var/old_decals = T.decals ? T.decals.Copy() : null
|
||||
var/old_dir1 = Origin.dir
|
||||
var/old_icon_state1 = Origin.icon_state
|
||||
var/old_icon1 = Origin.icon
|
||||
var/old_underlays = Origin.underlays.Copy()
|
||||
var/old_decals = Origin.decals ? Origin.decals.Copy() : null
|
||||
|
||||
X = B.PlaceOnTop(T.type)
|
||||
X = Destination.PlaceOnTop(Origin.type)
|
||||
X.setDir(old_dir1)
|
||||
X.icon_state = old_icon_state1
|
||||
X.icon = old_icon1
|
||||
X.copy_overlays(T, TRUE)
|
||||
X.copy_overlays(Origin, TRUE)
|
||||
X.underlays = old_underlays
|
||||
X.decals = old_decals
|
||||
|
||||
//Move the air from source to dest
|
||||
var/turf/simulated/ST = T
|
||||
if(istype(ST))
|
||||
/// Move the air from source to dest.
|
||||
var/turf/simulated/ST = Origin
|
||||
if (istype(ST))
|
||||
var/turf/simulated/SX = X
|
||||
if(!SX.air)
|
||||
SX.make_air()
|
||||
SX.air.copy_from(ST.copy_cell_volume())
|
||||
|
||||
var/z_level_change = FALSE
|
||||
if(T.z != X.z)
|
||||
if (Origin.z != X.z)
|
||||
z_level_change = TRUE
|
||||
|
||||
// Move the objects. Not forceMove because the object isn't "moving" really, it's supposed to be on the "same" turf.
|
||||
for(var/obj/O in T)
|
||||
for(var/obj/O in Origin)
|
||||
if(O.flags & ATOM_ABSTRACT)
|
||||
continue
|
||||
O.loc = X
|
||||
O.update_light()
|
||||
if(z_level_change) // The objects still need to know if their z-level changed.
|
||||
O.onTransitZ(T.z, X.z)
|
||||
// The objects still need to know if their z-level changed.
|
||||
if (z_level_change)
|
||||
O.onTransitZ(Origin.z, X.z)
|
||||
|
||||
// Move the mobs unless it's an AI eye or other eye type.
|
||||
for(var/mob/M in T)
|
||||
if(M.flags & ATOM_ABSTRACT)
|
||||
for(var/mob/M in Origin)
|
||||
if (M.flags & ATOM_ABSTRACT)
|
||||
continue
|
||||
if (isEye(M))
|
||||
// If we need to check for more mobs, I'll add a variable.
|
||||
continue
|
||||
if(isEye(M)) continue // If we need to check for more mobs, I'll add a variable
|
||||
M.loc = X
|
||||
|
||||
if(z_level_change) // Same goes for mobs.
|
||||
M.onTransitZ(T.z, X.z)
|
||||
// Same goes for mobs.
|
||||
if (z_level_change)
|
||||
M.onTransitZ(Origin.z, X.z)
|
||||
|
||||
if(istype(M, /mob/living))
|
||||
if (istype(M, /mob/living))
|
||||
var/mob/living/LM = M
|
||||
LM.check_shadow() // Need to check their Z-shadow, which is normally done in forceMove().
|
||||
// Need to check their Z-shadow, which is normally done in forceMove().
|
||||
LM.check_shadow()
|
||||
|
||||
if(turftoleave)
|
||||
T.ChangeTurf(turftoleave)
|
||||
if (turftoleave)
|
||||
Origin.ChangeTurf(turftoleave)
|
||||
else
|
||||
T.ScrapeAway()
|
||||
Origin.ScrapeAway()
|
||||
|
||||
return TRUE
|
||||
|
||||
//Used for border objects. This returns true if this atom is on the border between the two specified turfs
|
||||
//This assumes that the atom is located inside the target turf
|
||||
/atom/proc/is_between_turfs(var/turf/origin, var/turf/target)
|
||||
/**
|
||||
* Used for border objects. This returns true if this atom is on the border between the two specified turfs.
|
||||
* This assumes that the atom is located inside the target turf.
|
||||
*/
|
||||
/atom/proc/is_between_turfs(turf/origin, turf/target)
|
||||
if (flags & ON_BORDER)
|
||||
var/testdir = get_dir(target, origin)
|
||||
return (dir & testdir)
|
||||
|
||||
@@ -1,359 +0,0 @@
|
||||
/*
|
||||
* Holds procs designed to change one type of value, into another.
|
||||
* Contains:
|
||||
* hex2num & num2hex
|
||||
* text2list & list2text
|
||||
* file2list
|
||||
* angle2dir
|
||||
* angle2text
|
||||
* worldtime2text
|
||||
*/
|
||||
|
||||
//Returns an integer given a hex input, supports negative values "-ff"
|
||||
//skips preceding invalid characters
|
||||
//breaks when hittin invalid characters thereafter
|
||||
// If safe=TRUE, returns null on incorrect input strings instead of CRASHing
|
||||
/proc/hex2num(hex, safe=FALSE)
|
||||
. = 0
|
||||
var/place = 1
|
||||
for(var/i in length(hex) to 1 step -1)
|
||||
var/num = text2ascii(hex, i)
|
||||
switch(num)
|
||||
if(48 to 57)
|
||||
num -= 48 //0-9
|
||||
if(97 to 102)
|
||||
num -= 87 //a-f
|
||||
if(65 to 70)
|
||||
num -= 55 //A-F
|
||||
if(45)
|
||||
return . * -1 // -
|
||||
else
|
||||
if(safe)
|
||||
return null
|
||||
else
|
||||
CRASH("Malformed hex number")
|
||||
|
||||
. += num * place
|
||||
place *= 16
|
||||
|
||||
|
||||
// Returns the hex value of a number given a value assumed to be a base-ten value
|
||||
/proc/num2hex(num, padlength)
|
||||
var/global/list/hexdigits = list("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F")
|
||||
|
||||
. = ""
|
||||
while(num > 0)
|
||||
var/hexdigit = hexdigits[(num & 0xF) + 1]
|
||||
. = "[hexdigit][.]"
|
||||
num >>= 4 //go to the next half-byte
|
||||
|
||||
//pad with zeroes
|
||||
var/left = padlength - length(.)
|
||||
while (left-- > 0)
|
||||
. = "0[.]"
|
||||
|
||||
/proc/text2numlist(text, delimiter="\n")
|
||||
var/list/num_list = list()
|
||||
for(var/x in splittext(text, delimiter))
|
||||
num_list += text2num(x)
|
||||
return num_list
|
||||
|
||||
// Turns a direction into text
|
||||
/proc/num2dir(direction)
|
||||
switch (direction)
|
||||
if (1.0) return NORTH
|
||||
if (2.0) return SOUTH
|
||||
if (4.0) return EAST
|
||||
if (8.0) return WEST
|
||||
else
|
||||
log_world("UNKNOWN DIRECTION: [direction]")
|
||||
|
||||
/// Splits the text of a file at seperator and returns them in a list.
|
||||
/// Returns an empty list if the file doesn't exist
|
||||
/world/proc/file2list(filename, seperator="\n", trim = TRUE)
|
||||
if (trim)
|
||||
return splittext(trim(file2text(filename)),seperator)
|
||||
return splittext(file2text(filename),seperator)
|
||||
|
||||
// Turns a direction into text
|
||||
/proc/dir2text(direction)
|
||||
switch (direction)
|
||||
if (NORTH) return "north"
|
||||
if (SOUTH) return "south"
|
||||
if (EAST) return "east"
|
||||
if (WEST) return "west"
|
||||
if (NORTHEAST) return "northeast"
|
||||
if (SOUTHEAST) return "southeast"
|
||||
if (NORTHWEST) return "northwest"
|
||||
if (SOUTHWEST) return "southwest"
|
||||
if (UP) return "up"
|
||||
if (DOWN) return "down"
|
||||
|
||||
// Turns text into proper directions
|
||||
/proc/text2dir(direction)
|
||||
switch (uppertext(direction))
|
||||
if ("NORTH") return 1
|
||||
if ("SOUTH") return 2
|
||||
if ("EAST") return 4
|
||||
if ("WEST") return 8
|
||||
if ("NORTHEAST") return 5
|
||||
if ("NORTHWEST") return 9
|
||||
if ("SOUTHEAST") return 6
|
||||
if ("SOUTHWEST") return 10
|
||||
|
||||
// Converts an angle (degrees) into an ss13 direction
|
||||
/proc/angle2dir(var/degree)
|
||||
degree = (degree + 22.5) % 365 // 22.5 = 45 / 2
|
||||
if (degree < 45) return NORTH
|
||||
if (degree < 90) return NORTHEAST
|
||||
if (degree < 135) return EAST
|
||||
if (degree < 180) return SOUTHEAST
|
||||
if (degree < 225) return SOUTH
|
||||
if (degree < 270) return SOUTHWEST
|
||||
if (degree < 315) return WEST
|
||||
return NORTH|WEST
|
||||
|
||||
// Returns the north-zero clockwise angle in degrees, given a direction
|
||||
/proc/dir2angle(var/D)
|
||||
switch (D)
|
||||
if (NORTH) return 0
|
||||
if (SOUTH) return 180
|
||||
if (EAST) return 90
|
||||
if (WEST) return 270
|
||||
if (NORTHEAST) return 45
|
||||
if (SOUTHEAST) return 135
|
||||
if (NORTHWEST) return 315
|
||||
if (SOUTHWEST) return 225
|
||||
|
||||
// Returns the angle in english
|
||||
/proc/angle2text(var/degree)
|
||||
return dir2text(angle2dir(degree))
|
||||
|
||||
// Converts a blend_mode constant to one acceptable to icon.Blend()
|
||||
/proc/blendMode2iconMode(blend_mode)
|
||||
switch (blend_mode)
|
||||
if (BLEND_MULTIPLY) return ICON_MULTIPLY
|
||||
if (BLEND_ADD) return ICON_ADD
|
||||
if (BLEND_SUBTRACT) return ICON_SUBTRACT
|
||||
else return ICON_OVERLAY
|
||||
|
||||
// Converts a rights bitfield into a string
|
||||
/proc/rights2text(rights,seperator="")
|
||||
if (rights & R_BUILDMODE) . += "[seperator]+BUILDMODE"
|
||||
if (rights & R_ADMIN) . += "[seperator]+ADMIN"
|
||||
if (rights & R_BAN) . += "[seperator]+BAN"
|
||||
if (rights & R_FUN) . += "[seperator]+FUN"
|
||||
if (rights & R_SERVER) . += "[seperator]+SERVER"
|
||||
if (rights & R_DEBUG) . += "[seperator]+DEBUG"
|
||||
if (rights & R_POSSESS) . += "[seperator]+POSSESS"
|
||||
if (rights & R_PERMISSIONS) . += "[seperator]+PERMISSIONS"
|
||||
if (rights & R_STEALTH) . += "[seperator]+STEALTH"
|
||||
if (rights & R_REJUVINATE) . += "[seperator]+REJUVINATE"
|
||||
if (rights & R_VAREDIT) . += "[seperator]+VAREDIT"
|
||||
if (rights & R_SOUNDS) . += "[seperator]+SOUND"
|
||||
if (rights & R_SPAWN) . += "[seperator]+SPAWN"
|
||||
if (rights & R_MOD) . += "[seperator]+MODERATOR"
|
||||
if (rights & R_EVENT) . += "[seperator]+EVENT"
|
||||
return .
|
||||
|
||||
// Converts a hexadecimal color (e.g. #FF0050) to a list of numbers for red, green, and blue (e.g. list(255,0,80) ).
|
||||
/proc/hex2rgb(hex)
|
||||
// Strips the starting #, in case this is ever supplied without one, so everything doesn't break.
|
||||
if(findtext(hex,"#",1,2))
|
||||
hex = copytext(hex, 2)
|
||||
return list(hex2rgb_r(hex), hex2rgb_g(hex), hex2rgb_b(hex))
|
||||
|
||||
// The three procs below require that the '#' part of the hex be stripped, which hex2rgb() does automatically.
|
||||
/proc/hex2rgb_r(hex)
|
||||
var/hex_to_work_on = copytext(hex,1,3)
|
||||
return hex2num(hex_to_work_on)
|
||||
|
||||
/proc/hex2rgb_g(hex)
|
||||
var/hex_to_work_on = copytext(hex,3,5)
|
||||
return hex2num(hex_to_work_on)
|
||||
|
||||
/proc/hex2rgb_b(hex)
|
||||
var/hex_to_work_on = copytext(hex,5,7)
|
||||
return hex2num(hex_to_work_on)
|
||||
|
||||
// heat2color functions. Adapted from: http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
|
||||
/proc/heat2color(temp)
|
||||
return rgb(heat2color_r(temp), heat2color_g(temp), heat2color_b(temp))
|
||||
|
||||
/proc/heat2color_r(temp)
|
||||
temp /= 100
|
||||
if(temp <= 66)
|
||||
. = 255
|
||||
else
|
||||
. = max(0, min(255, 329.698727446 * (temp - 60) ** -0.1332047592))
|
||||
|
||||
/proc/heat2color_g(temp)
|
||||
temp /= 100
|
||||
if(temp <= 66)
|
||||
. = max(0, min(255, 99.4708025861 * log(temp) - 161.1195681661))
|
||||
else
|
||||
. = max(0, min(255, 288.1221695283 * ((temp - 60) ** -0.0755148492)))
|
||||
|
||||
/proc/heat2color_b(temp)
|
||||
temp /= 100
|
||||
if(temp >= 66)
|
||||
. = 255
|
||||
else
|
||||
if(temp <= 16)
|
||||
. = 0
|
||||
else
|
||||
. = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307))
|
||||
|
||||
// Very ugly, BYOND doesn't support unix time and rounding errors make it really hard to convert it to BYOND time.
|
||||
// returns "YYYY-MM-DD" by default
|
||||
/proc/unix2date(timestamp, seperator = "-")
|
||||
if(timestamp < 0)
|
||||
return 0 //Do not accept negative values
|
||||
|
||||
var/const/dayInSeconds = 86400 //60secs*60mins*24hours
|
||||
var/const/daysInYear = 365 //Non Leap Year
|
||||
var/const/daysInLYear = daysInYear + 1//Leap year
|
||||
var/days = round(timestamp / dayInSeconds) //Days passed since UNIX Epoc
|
||||
var/year = 1970 //Unix Epoc begins 1970-01-01
|
||||
var/tmpDays = days + 1 //If passed (timestamp < dayInSeconds), it will return 0, so add 1
|
||||
var/monthsInDays = list() //Months will be in here ***Taken from the PHP source code***
|
||||
var/month = 1 //This will be the returned MONTH NUMBER.
|
||||
var/day //This will be the returned day number.
|
||||
|
||||
while(tmpDays > daysInYear) //Start adding years to 1970
|
||||
year++
|
||||
if(isLeap(year))
|
||||
tmpDays -= daysInLYear
|
||||
else
|
||||
tmpDays -= daysInYear
|
||||
|
||||
if(isLeap(year)) //The year is a leap year
|
||||
monthsInDays = list(-1,30,59,90,120,151,181,212,243,273,304,334)
|
||||
else
|
||||
monthsInDays = list(0,31,59,90,120,151,181,212,243,273,304,334)
|
||||
|
||||
var/mDays = 0;
|
||||
var/monthIndex = 0;
|
||||
|
||||
for(var/m in monthsInDays)
|
||||
monthIndex++
|
||||
if(tmpDays > m)
|
||||
mDays = m
|
||||
month = monthIndex
|
||||
|
||||
day = tmpDays - mDays //Setup the date
|
||||
|
||||
return "[year][seperator][((month < 10) ? "0[month]" : month)][seperator][((day < 10) ? "0[day]" : day)]"
|
||||
|
||||
/proc/isLeap(y)
|
||||
return ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
|
||||
|
||||
/proc/type2parent(child)
|
||||
var/string_type = "[child]"
|
||||
var/last_slash = findlasttext(string_type, "/")
|
||||
if(last_slash == 1)
|
||||
switch(child)
|
||||
if(/datum)
|
||||
return null
|
||||
if(/obj, /mob)
|
||||
return /atom/movable
|
||||
if(/area, /turf)
|
||||
return /atom
|
||||
else
|
||||
return /datum
|
||||
return text2path(copytext(string_type, 1, last_slash))
|
||||
|
||||
//returns a string the last bit of a type, without the preceeding '/'
|
||||
/proc/type2top(the_type)
|
||||
//handle the builtins manually
|
||||
if(!ispath(the_type))
|
||||
return
|
||||
switch(the_type)
|
||||
if(/datum)
|
||||
return "datum"
|
||||
if(/atom)
|
||||
return "atom"
|
||||
if(/obj)
|
||||
return "obj"
|
||||
if(/mob)
|
||||
return "mob"
|
||||
if(/area)
|
||||
return "area"
|
||||
if(/turf)
|
||||
return "turf"
|
||||
else //regex everything else (works for /proc too)
|
||||
return lowertext(replacetext("[the_type]", "[type2parent(the_type)]/", ""))
|
||||
|
||||
/// Return html to load a url.
|
||||
/// for use inside of browse() calls to html assets that might be loaded on a cdn.
|
||||
/proc/url2htmlloader(url)
|
||||
return {"<html><head><meta http-equiv="refresh" content="0;URL='[url]'"/></head><body onLoad="parent.location='[url]'"></body></html>"}
|
||||
|
||||
// Converts a string into ascii then converts it into hexadecimal.
|
||||
/proc/strtohex(str)
|
||||
if(!istext(str)||!str)
|
||||
return
|
||||
var/r
|
||||
var/c
|
||||
for(var/i = 1 to length(str))
|
||||
c= text2ascii(str,i)
|
||||
r+= num2hex(c)
|
||||
return r
|
||||
|
||||
// Decodes hexadecimal ascii into a raw byte string.
|
||||
// If safe=TRUE, returns null on incorrect input strings instead of CRASHing
|
||||
/proc/hextostr(str, safe=FALSE)
|
||||
if(!istext(str)||!str)
|
||||
return
|
||||
var/r
|
||||
var/c
|
||||
for(var/i = 1 to length(str)/2)
|
||||
c = hex2num(copytext(str,i*2-1,i*2+1), safe)
|
||||
if(isnull(c))
|
||||
return null
|
||||
r += ascii2text(c)
|
||||
return r
|
||||
|
||||
|
||||
//This is a weird one:
|
||||
//It returns a list of all var names found in the string
|
||||
//These vars must be in the [var_name] format
|
||||
//It's only a proc because it's used in more than one place
|
||||
|
||||
//Takes a string and a datum
|
||||
//The string is well, obviously the string being checked
|
||||
//The datum is used as a source for var names, to check validity
|
||||
//Otherwise every single word could technically be a variable!
|
||||
/proc/string2listofvars(var/t_string, var/datum/var_source)
|
||||
if(!t_string || !var_source)
|
||||
return list()
|
||||
|
||||
. = list()
|
||||
|
||||
var/var_found = findtext(t_string,"\[") //Not the actual variables, just a generic "should we even bother" check
|
||||
if(var_found)
|
||||
//Find var names
|
||||
|
||||
// "A dog said hi [name]!"
|
||||
// splittext() --> list("A dog said hi ","name]!"
|
||||
// jointext() --> "A dog said hi name]!"
|
||||
// splittext() --> list("A","dog","said","hi","name]!")
|
||||
|
||||
t_string = replacetext(t_string,"\[","\[ ")//Necessary to resolve "word[var_name]" scenarios
|
||||
var/list/list_value = splittext(t_string,"\[")
|
||||
var/intermediate_stage = jointext(list_value, null)
|
||||
|
||||
list_value = splittext(intermediate_stage," ")
|
||||
for(var/value in list_value)
|
||||
if(findtext(value,"]"))
|
||||
value = splittext(value,"]") //"name]!" --> list("name","!")
|
||||
for(var/A in value)
|
||||
if(var_source.vars.Find(A))
|
||||
. += A
|
||||
|
||||
/proc/get_end_section_of_type(type)
|
||||
var/strtype = "[type]"
|
||||
var/delim_pos = findlasttext(strtype, "/")
|
||||
if(delim_pos == 0)
|
||||
return strtype
|
||||
return copytext(strtype, delim_pos)
|
||||
@@ -1,37 +1,50 @@
|
||||
//colour formats
|
||||
/**
|
||||
* Convert RBG to HSL
|
||||
*/
|
||||
/proc/rgb2hsl(red, green, blue)
|
||||
red /= 255;green /= 255;blue /= 255;
|
||||
var/max = max(red,green,blue)
|
||||
var/min = min(red,green,blue)
|
||||
var/range = max-min
|
||||
red /= 255
|
||||
green /= 255
|
||||
blue /= 255
|
||||
|
||||
var/hue=0;var/saturation=0;var/lightness=0;
|
||||
lightness = (max + min)/2
|
||||
var/max = max(red, green, blue)
|
||||
var/min = min(red, green, blue)
|
||||
var/range = max - min
|
||||
|
||||
var/hue = 0
|
||||
var/saturation = 0
|
||||
var/lightness = 0
|
||||
|
||||
lightness = (max + min) / 2
|
||||
if(range != 0)
|
||||
if(lightness < 0.5)
|
||||
saturation = range/(max+min)
|
||||
saturation = range / (max + min)
|
||||
else
|
||||
saturation = range/(2-max-min)
|
||||
saturation = range / (2 - max - min)
|
||||
|
||||
var/dred = ((max-red)/(6*max)) + 0.5
|
||||
var/dgreen = ((max-green)/(6*max)) + 0.5
|
||||
var/dblue = ((max-blue)/(6*max)) + 0.5
|
||||
var/dred = ((max - red) / (6 * max)) + 0.5
|
||||
var/dgreen = ((max - green) / (6 * max)) + 0.5
|
||||
var/dblue = ((max - blue) / (6 * max)) + 0.5
|
||||
|
||||
if(max==red)
|
||||
if(max == red)
|
||||
hue = dblue - dgreen
|
||||
else if(max==green)
|
||||
hue = dred - dblue + (1/3)
|
||||
else if(max == green)
|
||||
hue = dred - dblue + (1 / 3)
|
||||
else
|
||||
hue = dgreen - dred + (2/3)
|
||||
hue = dgreen - dred + (2 / 3)
|
||||
if(hue < 0)
|
||||
hue++
|
||||
else if(hue > 1)
|
||||
hue--
|
||||
|
||||
return list(hue, saturation, lightness)
|
||||
|
||||
/**
|
||||
* Convert HSL to RGB
|
||||
*/
|
||||
/proc/hsl2rgb(hue, saturation, lightness)
|
||||
var/red;var/green;var/blue;
|
||||
var/red
|
||||
var/green
|
||||
var/blue
|
||||
|
||||
if(saturation == 0)
|
||||
red = lightness * 255
|
||||
green = red
|
||||
@@ -39,34 +52,48 @@
|
||||
else
|
||||
var/a;var/b;
|
||||
if(lightness < 0.5)
|
||||
b = lightness*(1+saturation)
|
||||
b = lightness * (1 + saturation)
|
||||
else
|
||||
b = (lightness+saturation) - (saturation*lightness)
|
||||
a = 2*lightness - b
|
||||
b = (lightness + saturation) - (saturation * lightness)
|
||||
a = 2 * lightness - b
|
||||
|
||||
red = round(255 * hue2rgb(a, b, hue+(1/3)), 1)
|
||||
red = round(255 * hue2rgb(a, b, hue + (1/3)), 1)
|
||||
green = round(255 * hue2rgb(a, b, hue), 1)
|
||||
blue = round(255 * hue2rgb(a, b, hue-(1/3)), 1)
|
||||
blue = round(255 * hue2rgb(a, b, hue - (1/3)), 1)
|
||||
|
||||
return list(red, green, blue)
|
||||
|
||||
/**
|
||||
* Convert hue to RGB
|
||||
*/
|
||||
/proc/hue2rgb(a, b, hue)
|
||||
if(hue < 0)
|
||||
hue++
|
||||
else if(hue > 1)
|
||||
hue--
|
||||
if(6*hue < 1)
|
||||
return (a+(b-a)*6*hue)
|
||||
return (a + (b - a) * 6 * hue)
|
||||
if(2*hue < 1)
|
||||
return b
|
||||
if(3*hue < 2)
|
||||
return (a+(b-a)*((2/3)-hue)*6)
|
||||
return (a + (b - a) * ((2 / 3) - hue) * 6)
|
||||
return a
|
||||
|
||||
//adapted from http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
|
||||
/**
|
||||
* Convert Kelvin to RGB
|
||||
*
|
||||
* Adapted from http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
|
||||
*/
|
||||
/proc/heat2colour(temp)
|
||||
return rgb(heat2colour_r(temp), heat2colour_g(temp), heat2colour_b(temp))
|
||||
return rgb(
|
||||
heat2colour_r(temp),
|
||||
heat2colour_g(temp),
|
||||
heat2colour_b(temp),
|
||||
)
|
||||
|
||||
/**
|
||||
* Convert Kelvin for the Red channel
|
||||
*/
|
||||
/proc/heat2colour_r(temp)
|
||||
temp /= 100
|
||||
if(temp <= 66)
|
||||
@@ -74,6 +101,9 @@
|
||||
else
|
||||
. = max(0, min(255, 329.698727446 * (temp - 60) ** -0.1332047592))
|
||||
|
||||
/**
|
||||
* Convert Kelvin for the Green channel
|
||||
*/
|
||||
/proc/heat2colour_g(temp)
|
||||
temp /= 100
|
||||
if(temp <= 66)
|
||||
@@ -81,6 +111,9 @@
|
||||
else
|
||||
. = max(0, min(255, 288.1221685293 * ((temp - 60) ** -0.075148492)))
|
||||
|
||||
/**
|
||||
* Convert Kelvin for the Blue channel
|
||||
*/
|
||||
/proc/heat2colour_b(temp)
|
||||
temp /= 100
|
||||
if(temp >= 66)
|
||||
@@ -91,51 +124,9 @@
|
||||
else
|
||||
. = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307))
|
||||
|
||||
/proc/color2hex(color) //web colors
|
||||
if(!color)
|
||||
return "#000000"
|
||||
|
||||
switch(color)
|
||||
if("white")
|
||||
return "#FFFFFF"
|
||||
if("black")
|
||||
return "#000000"
|
||||
if("gray")
|
||||
return "#808080"
|
||||
if("brown")
|
||||
return "#A52A2A"
|
||||
if("red")
|
||||
return "#FF0000"
|
||||
if("darkred")
|
||||
return "#8B0000"
|
||||
if("crimson")
|
||||
return "#DC143C"
|
||||
if("orange")
|
||||
return "#FFA500"
|
||||
if("yellow")
|
||||
return "#FFFF00"
|
||||
if("green")
|
||||
return "#008000"
|
||||
if("lime")
|
||||
return "#00FF00"
|
||||
if("darkgreen")
|
||||
return "#006400"
|
||||
if("cyan")
|
||||
return "#00FFFF"
|
||||
if("blue")
|
||||
return "#0000FF"
|
||||
if("navy")
|
||||
return "#000080"
|
||||
if("teal")
|
||||
return "#008080"
|
||||
if("purple")
|
||||
return "#800080"
|
||||
if("indigo")
|
||||
return "#4B0082"
|
||||
else
|
||||
return "#FFFFFF"
|
||||
|
||||
//assumes format #RRGGBB #rrggbb
|
||||
/**
|
||||
* Assumes format #RRGGBB #rrggbb
|
||||
*/
|
||||
/proc/color_hex2num(A)
|
||||
if(!A || length(A) != length_char(A))
|
||||
return 0
|
||||
@@ -144,23 +135,60 @@
|
||||
var/B = hex2num(copytext(A, 6, 8))
|
||||
return R+G+B
|
||||
|
||||
//word of warning: using a matrix like this as a color value will simplify it back to a string after being set
|
||||
/**
|
||||
*! Word of warning:
|
||||
* Using a matrix like this as a color value will simplify it back to a string after being set.
|
||||
*/
|
||||
/proc/color_hex2color_matrix(string)
|
||||
var/length = length(string)
|
||||
if((length != 7 && length != 9) || length != length_char(string))
|
||||
return color_matrix_identity()
|
||||
var/r = hex2num(copytext(string, 2, 4))/255
|
||||
var/g = hex2num(copytext(string, 4, 6))/255
|
||||
var/b = hex2num(copytext(string, 6, 8))/255
|
||||
var/r = hex2num(copytext(string, 2, 4)) / 255
|
||||
var/g = hex2num(copytext(string, 4, 6)) / 255
|
||||
var/b = hex2num(copytext(string, 6, 8)) / 255
|
||||
var/a = 1
|
||||
if(length == 9)
|
||||
a = hex2num(copytext(string, 8, 10))/255
|
||||
a = hex2num(copytext(string, 8, 10)) / 255
|
||||
if(!isnum(r) || !isnum(g) || !isnum(b) || !isnum(a))
|
||||
return color_matrix_identity()
|
||||
return list(r,0,0,0, 0,g,0,0, 0,0,b,0, 0,0,0,a, 0,0,0,0)
|
||||
return list(
|
||||
r,0,0,0,0,
|
||||
g,0,0,0,0,
|
||||
b,0,0,0,0,
|
||||
a,0,0,0,0,
|
||||
)
|
||||
|
||||
//will drop all values not on the diagonal
|
||||
/**
|
||||
* Will drop all values not on the diagonal.
|
||||
*/
|
||||
/proc/color_matrix2color_hex(list/the_matrix)
|
||||
if(!istype(the_matrix) || the_matrix.len != 20)
|
||||
return "#ffffffff"
|
||||
return rgb(the_matrix[1]*255, the_matrix[6]*255, the_matrix[11]*255, the_matrix[16]*255)
|
||||
return rgb(
|
||||
the_matrix[1] * 255, // R
|
||||
the_matrix[6] * 255, // G
|
||||
the_matrix[11] * 255, // B
|
||||
the_matrix[16] * 255, // A
|
||||
)
|
||||
|
||||
/**
|
||||
* Converts a hexadecimal color (e.g. #FF0050) to a list of numbers for red, green, and blue (e.g. list(255,0,80) ).
|
||||
*/
|
||||
/proc/hex2rgb(hex)
|
||||
// Strips the starting #, in case this is ever supplied without one, so everything doesn't break.
|
||||
if(findtext(hex,"#",1,2))
|
||||
hex = copytext(hex, 2)
|
||||
return list(hex2rgb_r(hex), hex2rgb_g(hex), hex2rgb_b(hex))
|
||||
|
||||
//! The three procs below require that the '#' part of the hex be stripped, which hex2rgb() does automatically.
|
||||
/proc/hex2rgb_r(hex)
|
||||
var/hex_to_work_on = copytext(hex,1,3)
|
||||
return hex2num(hex_to_work_on)
|
||||
|
||||
/proc/hex2rgb_g(hex)
|
||||
var/hex_to_work_on = copytext(hex,3,5)
|
||||
return hex2num(hex_to_work_on)
|
||||
|
||||
/proc/hex2rgb_b(hex)
|
||||
var/hex_to_work_on = copytext(hex,5,7)
|
||||
return hex2num(hex_to_work_on)
|
||||
|
||||
499
code/__HELPERS/type2type/type2type.dm
Normal file
499
code/__HELPERS/type2type/type2type.dm
Normal file
@@ -0,0 +1,499 @@
|
||||
/**
|
||||
* Holds procs designed to change one type of value, into another.
|
||||
*
|
||||
* Contains:
|
||||
* * hex2num & num2hex
|
||||
* * text2list & list2text
|
||||
* * file2list
|
||||
* * angle2dir
|
||||
* * angle2text
|
||||
* * worldtime2text
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns an integer given a hex input, supports negative values "-ff"
|
||||
* skips preceding invalid characters
|
||||
* breaks when hittin invalid characters thereafter
|
||||
* If safe=TRUE, returns null on incorrect input strings instead of CRASHing
|
||||
*/
|
||||
/proc/hex2num(hex, safe=FALSE)
|
||||
. = 0
|
||||
var/place = 1
|
||||
for(var/i in length(hex) to 1 step -1)
|
||||
var/num = text2ascii(hex, i)
|
||||
switch(num)
|
||||
if(48 to 57)
|
||||
num -= 48 //0-9
|
||||
if(97 to 102)
|
||||
num -= 87 //a-f
|
||||
if(65 to 70)
|
||||
num -= 55 //A-F
|
||||
if(45)
|
||||
return . * -1 // -
|
||||
else
|
||||
if(safe)
|
||||
return null
|
||||
else
|
||||
CRASH("Malformed hex number")
|
||||
|
||||
. += num * place
|
||||
place *= 16
|
||||
|
||||
|
||||
/**
|
||||
* Returns the hex value of a number given a value assumed to be a base-ten value.
|
||||
*/
|
||||
/proc/num2hex(num, padlength)
|
||||
var/global/list/hexdigits = list("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F")
|
||||
|
||||
. = ""
|
||||
while(num > 0)
|
||||
var/hexdigit = hexdigits[(num & 0xF) + 1]
|
||||
. = "[hexdigit][.]"
|
||||
num >>= 4 //go to the next half-byte
|
||||
|
||||
//pad with zeroes
|
||||
var/left = padlength - length(.)
|
||||
while (left-- > 0)
|
||||
. = "0[.]"
|
||||
|
||||
/proc/text2numlist(text, delimiter="\n")
|
||||
var/list/num_list = list()
|
||||
for(var/x in splittext(text, delimiter))
|
||||
num_list += text2num(x)
|
||||
return num_list
|
||||
|
||||
/**
|
||||
* Turns a direction into text.
|
||||
*/
|
||||
/proc/num2dir(direction)
|
||||
switch (direction)
|
||||
if (1.0) return NORTH
|
||||
if (2.0) return SOUTH
|
||||
if (4.0) return EAST
|
||||
if (8.0) return WEST
|
||||
else
|
||||
log_world("UNKNOWN DIRECTION: [direction]")
|
||||
|
||||
/**
|
||||
* Splits the text of a file at seperator and returns them in a list.
|
||||
* Returns an empty list if the file doesn't exist.
|
||||
*/
|
||||
/world/proc/file2list(filename, seperator="\n", trim = TRUE)
|
||||
if (trim)
|
||||
return splittext(trim(file2text(filename)),seperator)
|
||||
return splittext(file2text(filename),seperator)
|
||||
|
||||
/**
|
||||
* Turns a direction into text.
|
||||
*/
|
||||
/proc/dir2text(direction)
|
||||
switch (direction)
|
||||
if (NORTH)
|
||||
return "north"
|
||||
if (SOUTH)
|
||||
return "south"
|
||||
if (EAST)
|
||||
return "east"
|
||||
if (WEST)
|
||||
return "west"
|
||||
if (NORTHEAST)
|
||||
return "northeast"
|
||||
if (SOUTHEAST)
|
||||
return "southeast"
|
||||
if (NORTHWEST)
|
||||
return "northwest"
|
||||
if (SOUTHWEST)
|
||||
return "southwest"
|
||||
if (UP)
|
||||
return "up"
|
||||
if (DOWN)
|
||||
return "down"
|
||||
|
||||
/**
|
||||
* Turns text into proper directions.
|
||||
*/
|
||||
/proc/text2dir(direction)
|
||||
switch (uppertext(direction))
|
||||
if ("NORTH")
|
||||
return 1
|
||||
if ("SOUTH")
|
||||
return 2
|
||||
if ("EAST")
|
||||
return 4
|
||||
if ("WEST")
|
||||
return 8
|
||||
if ("NORTHEAST")
|
||||
return 5
|
||||
if ("NORTHWEST")
|
||||
return 9
|
||||
if ("SOUTHEAST")
|
||||
return 6
|
||||
if ("SOUTHWEST")
|
||||
return 10
|
||||
|
||||
/**
|
||||
* Converts an angle (degrees) into an ss13 direction.
|
||||
*/
|
||||
/proc/angle2dir(degree)
|
||||
degree = (degree + 22.5) % 365 // 22.5 = 45 / 2
|
||||
if (degree < 45)
|
||||
return NORTH
|
||||
if (degree < 90)
|
||||
return NORTHEAST
|
||||
if (degree < 135)
|
||||
return EAST
|
||||
if (degree < 180)
|
||||
return SOUTHEAST
|
||||
if (degree < 225)
|
||||
return SOUTH
|
||||
if (degree < 270)
|
||||
return SOUTHWEST
|
||||
if (degree < 315)
|
||||
return WEST
|
||||
return NORTH|WEST
|
||||
|
||||
/**
|
||||
* Returns the north-zero clockwise angle in degrees, given a direction.
|
||||
*/
|
||||
/proc/dir2angle(direction)
|
||||
switch (direction)
|
||||
if (NORTH)
|
||||
return 0
|
||||
if (SOUTH)
|
||||
return 180
|
||||
if (EAST)
|
||||
return 90
|
||||
if (WEST)
|
||||
return 270
|
||||
if (NORTHEAST)
|
||||
return 45
|
||||
if (SOUTHEAST)
|
||||
return 135
|
||||
if (NORTHWEST)
|
||||
return 315
|
||||
if (SOUTHWEST)
|
||||
return 225
|
||||
|
||||
/**
|
||||
* Returns the angle in english.
|
||||
*/
|
||||
/proc/angle2text(degree)
|
||||
return dir2text(angle2dir(degree))
|
||||
|
||||
/**
|
||||
* Converts a blend_mode constant to one acceptable to icon.Blend()
|
||||
*/
|
||||
/proc/blendMode2iconMode(blend_mode)
|
||||
switch (blend_mode)
|
||||
if (BLEND_MULTIPLY)
|
||||
return ICON_MULTIPLY
|
||||
if (BLEND_ADD)
|
||||
return ICON_ADD
|
||||
if (BLEND_SUBTRACT)
|
||||
return ICON_SUBTRACT
|
||||
else
|
||||
return ICON_OVERLAY
|
||||
|
||||
/**
|
||||
* Converts a rights bitfield into a string.
|
||||
*/
|
||||
/proc/rights2text(rights,seperator="")
|
||||
if (rights & R_BUILDMODE)
|
||||
. += "[seperator]+BUILDMODE"
|
||||
if (rights & R_ADMIN)
|
||||
. += "[seperator]+ADMIN"
|
||||
if (rights & R_BAN)
|
||||
. += "[seperator]+BAN"
|
||||
if (rights & R_FUN)
|
||||
. += "[seperator]+FUN"
|
||||
if (rights & R_SERVER)
|
||||
. += "[seperator]+SERVER"
|
||||
if (rights & R_DEBUG)
|
||||
. += "[seperator]+DEBUG"
|
||||
if (rights & R_POSSESS)
|
||||
. += "[seperator]+POSSESS"
|
||||
if (rights & R_PERMISSIONS)
|
||||
. += "[seperator]+PERMISSIONS"
|
||||
if (rights & R_STEALTH)
|
||||
. += "[seperator]+STEALTH"
|
||||
if (rights & R_REJUVINATE)
|
||||
. += "[seperator]+REJUVINATE"
|
||||
if (rights & R_VAREDIT)
|
||||
. += "[seperator]+VAREDIT"
|
||||
if (rights & R_SOUNDS)
|
||||
. += "[seperator]+SOUND"
|
||||
if (rights & R_SPAWN)
|
||||
. += "[seperator]+SPAWN"
|
||||
if (rights & R_MOD)
|
||||
. += "[seperator]+MODERATOR"
|
||||
if (rights & R_EVENT)
|
||||
. += "[seperator]+EVENT"
|
||||
return .
|
||||
|
||||
// Very ugly, BYOND doesn't support unix time and rounding errors make it really hard to convert it to BYOND time.
|
||||
// returns "YYYY-MM-DD" by default
|
||||
/proc/unix2date(timestamp, seperator = "-")
|
||||
if(timestamp < 0)
|
||||
return 0 //Do not accept negative values
|
||||
|
||||
var/const/dayInSeconds = 86400 //60secs*60mins*24hours
|
||||
var/const/daysInYear = 365 //Non Leap Year
|
||||
var/const/daysInLYear = daysInYear + 1//Leap year
|
||||
var/days = round(timestamp / dayInSeconds) //Days passed since UNIX Epoc
|
||||
var/year = 1970 //Unix Epoc begins 1970-01-01
|
||||
var/tmpDays = days + 1 //If passed (timestamp < dayInSeconds), it will return 0, so add 1
|
||||
var/monthsInDays = list() //Months will be in here ***Taken from the PHP source code***
|
||||
var/month = 1 //This will be the returned MONTH NUMBER.
|
||||
var/day //This will be the returned day number.
|
||||
|
||||
while(tmpDays > daysInYear) //Start adding years to 1970
|
||||
year++
|
||||
if(isLeap(year))
|
||||
tmpDays -= daysInLYear
|
||||
else
|
||||
tmpDays -= daysInYear
|
||||
|
||||
if(isLeap(year)) //The year is a leap year
|
||||
monthsInDays = list(-1,30,59,90,120,151,181,212,243,273,304,334)
|
||||
else
|
||||
monthsInDays = list(0,31,59,90,120,151,181,212,243,273,304,334)
|
||||
|
||||
var/mDays = 0;
|
||||
var/monthIndex = 0;
|
||||
|
||||
for(var/m in monthsInDays)
|
||||
monthIndex++
|
||||
if(tmpDays > m)
|
||||
mDays = m
|
||||
month = monthIndex
|
||||
|
||||
day = tmpDays - mDays //Setup the date
|
||||
|
||||
return "[year][seperator][((month < 10) ? "0[month]" : month)][seperator][((day < 10) ? "0[day]" : day)]"
|
||||
|
||||
/proc/isLeap(y)
|
||||
return ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
|
||||
|
||||
/proc/type2parent(child)
|
||||
var/string_type = "[child]"
|
||||
var/last_slash = findlasttext(string_type, "/")
|
||||
if(last_slash == 1)
|
||||
switch(child)
|
||||
if(/datum)
|
||||
return null
|
||||
if(/obj, /mob)
|
||||
return /atom/movable
|
||||
if(/area, /turf)
|
||||
return /atom
|
||||
else
|
||||
return /datum
|
||||
return text2path(copytext(string_type, 1, last_slash))
|
||||
|
||||
//returns a string the last bit of a type, without the preceeding '/'
|
||||
/proc/type2top(the_type)
|
||||
//handle the builtins manually
|
||||
if(!ispath(the_type))
|
||||
return
|
||||
switch(the_type)
|
||||
if(/datum)
|
||||
return "datum"
|
||||
if(/atom)
|
||||
return "atom"
|
||||
if(/obj)
|
||||
return "obj"
|
||||
if(/mob)
|
||||
return "mob"
|
||||
if(/area)
|
||||
return "area"
|
||||
if(/turf)
|
||||
return "turf"
|
||||
else //regex everything else (works for /proc too)
|
||||
return lowertext(replacetext("[the_type]", "[type2parent(the_type)]/", ""))
|
||||
|
||||
/// Return html to load a url.
|
||||
/// for use inside of browse() calls to html assets that might be loaded on a cdn.
|
||||
/proc/url2htmlloader(url)
|
||||
return {"<html><head><meta http-equiv="refresh" content="0;URL='[url]'"/></head><body onLoad="parent.location='[url]'"></body></html>"}
|
||||
|
||||
// Converts a string into ascii then converts it into hexadecimal.
|
||||
/proc/strtohex(str)
|
||||
if(!istext(str)||!str)
|
||||
return
|
||||
var/r
|
||||
var/c
|
||||
for(var/i = 1 to length(str))
|
||||
c= text2ascii(str,i)
|
||||
r+= num2hex(c)
|
||||
return r
|
||||
|
||||
// Decodes hexadecimal ascii into a raw byte string.
|
||||
// If safe=TRUE, returns null on incorrect input strings instead of CRASHing
|
||||
/proc/hextostr(str, safe=FALSE)
|
||||
if(!istext(str)||!str)
|
||||
return
|
||||
var/r
|
||||
var/c
|
||||
for(var/i = 1 to length(str)/2)
|
||||
c = hex2num(copytext(str,i*2-1,i*2+1), safe)
|
||||
if(isnull(c))
|
||||
return null
|
||||
r += ascii2text(c)
|
||||
return r
|
||||
|
||||
|
||||
/**
|
||||
*! This is a weird one:
|
||||
* It returns a list of all var names found in the string
|
||||
* These vars must be in the [var_name] format
|
||||
* It's only a proc because it's used in more than one place.
|
||||
*
|
||||
* Takes a string and a datum
|
||||
* The string is well, obviously the string being checked
|
||||
* The datum is used as a source for var names, to check validity
|
||||
* Otherwise every single word could technically be a variable!
|
||||
*/
|
||||
/proc/string2listofvars(t_string, datum/var_source)
|
||||
if(!t_string || !var_source)
|
||||
return list()
|
||||
|
||||
. = list()
|
||||
|
||||
var/var_found = findtext(t_string,"\[") //Not the actual variables, just a generic "should we even bother" check
|
||||
if(var_found)
|
||||
//Find var names
|
||||
|
||||
// "A dog said hi [name]!"
|
||||
// splittext() --> list("A dog said hi ","name]!"
|
||||
// jointext() --> "A dog said hi name]!"
|
||||
// splittext() --> list("A","dog","said","hi","name]!")
|
||||
|
||||
t_string = replacetext(t_string,"\[","\[ ")//Necessary to resolve "word[var_name]" scenarios
|
||||
var/list/list_value = splittext(t_string,"\[")
|
||||
var/intermediate_stage = jointext(list_value, null)
|
||||
|
||||
list_value = splittext(intermediate_stage," ")
|
||||
for(var/value in list_value)
|
||||
if(findtext(value,"]"))
|
||||
value = splittext(value,"]") //"name]!" --> list("name","!")
|
||||
for(var/A in value)
|
||||
if(var_source.vars.Find(A))
|
||||
. += A
|
||||
|
||||
/proc/get_end_section_of_type(type)
|
||||
var/strtype = "[type]"
|
||||
var/delim_pos = findlasttext(strtype, "/")
|
||||
if(delim_pos == 0)
|
||||
return strtype
|
||||
return copytext(strtype, delim_pos)
|
||||
|
||||
|
||||
/**
|
||||
* list2text - takes delimiter and returns text
|
||||
*
|
||||
* Concatenates a list of strings into a single string. A seperator may optionally be provided.
|
||||
*/
|
||||
/proc/list2text(list/ls, sep)
|
||||
if (ls.len <= 1) // Early-out code for empty or singleton lists.
|
||||
return ls.len ? ls[1] : ""
|
||||
|
||||
var/l = ls.len // Made local for sanic speed.
|
||||
var/i = 0 // Incremented every time a list index is accessed.
|
||||
|
||||
if (sep <> null)
|
||||
// Macros expand to long argument lists like so: sep, ls[++i], sep, ls[++i], sep, ls[++i], etc...
|
||||
#define S1 sep, ls[++i]
|
||||
#define S4 S1, S1, S1, S1
|
||||
#define S16 S4, S4, S4, S4
|
||||
#define S64 S16, S16, S16, S16
|
||||
|
||||
. = "[ls[++i]]" // Make sure the initial element is converted to text.
|
||||
|
||||
// Having the small concatenations come before the large ones boosted speed by an average of at least 5%.
|
||||
if (l-1 & 0x01) // 'i' will always be 1 here.
|
||||
. = text("[][][]", ., 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)
|
||||
. = text("[][][][][][][][][]", ., S4) // And so on....
|
||||
if (l-i & 0x08)
|
||||
. = text("[][][][][][][][][][][][][][][][][]", ., S4, S4)
|
||||
if (l-i & 0x10)
|
||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16)
|
||||
if (l-i & 0x20)
|
||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16, S16)
|
||||
if (l-i & 0x40)
|
||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64)
|
||||
while (l > i) // Chomp through the rest of the list, 128 elements at a time.
|
||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64)
|
||||
|
||||
#undef S64
|
||||
#undef S16
|
||||
#undef S4
|
||||
#undef S1
|
||||
else
|
||||
// Macros expand to long argument lists like so: ls[++i], ls[++i], ls[++i], etc...
|
||||
#define S1 ls[++i]
|
||||
#define S4 S1, S1, S1, S1
|
||||
#define S16 S4, S4, S4, S4
|
||||
#define S64 S16, S16, S16, S16
|
||||
|
||||
. = "[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.
|
||||
if (l-i & 0x02)
|
||||
. = text("[][][]", ., S1, S1) // Append 2 elements if the remaining elements are not a multiple of 4.
|
||||
if (l-i & 0x04)
|
||||
. = text("[][][][][]", ., S4) // And so on...
|
||||
if (l-i & 0x08)
|
||||
. = text("[][][][][][][][][]", ., S4, S4)
|
||||
if (l-i & 0x10)
|
||||
. = text("[][][][][][][][][][][][][][][][][]", ., S16)
|
||||
if (l-i & 0x20)
|
||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16, S16)
|
||||
if (l-i & 0x40)
|
||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64)
|
||||
while (l > i) // Chomp through the rest of the list, 128 elements at a time.
|
||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64)
|
||||
|
||||
#undef S64
|
||||
#undef S16
|
||||
#undef S4
|
||||
#undef S1
|
||||
|
||||
/**
|
||||
* text2list - takes delimiter, and creates list
|
||||
*
|
||||
* Converts a string into a list by splitting the string at each delimiter found. (discarding the seperator)
|
||||
*/
|
||||
|
||||
/proc/text2list(text, delimiter="\n")
|
||||
var/delim_len = length(delimiter)
|
||||
if (delim_len < 1)
|
||||
return list(text)
|
||||
|
||||
. = list()
|
||||
var/last_found = 1
|
||||
var/found
|
||||
|
||||
do
|
||||
found = findtext(text, delimiter, last_found, 0)
|
||||
. += copytext(text, last_found, found)
|
||||
last_found = found + delim_len
|
||||
while (found)
|
||||
@@ -1,106 +0,0 @@
|
||||
/**
|
||||
* Contains VOREStation type2type functions
|
||||
* list2text - takes delimiter and returns text
|
||||
* text2list - takes delimiter, and creates list
|
||||
*/
|
||||
|
||||
// Concatenates a list of strings into a single string. A seperator may optionally be provided.
|
||||
/proc/list2text(list/ls, sep)
|
||||
if (ls.len <= 1) // Early-out code for empty or singleton lists.
|
||||
return ls.len ? ls[1] : ""
|
||||
|
||||
var/l = ls.len // Made local for sanic speed.
|
||||
var/i = 0 // Incremented every time a list index is accessed.
|
||||
|
||||
if (sep <> null)
|
||||
// Macros expand to long argument lists like so: sep, ls[++i], sep, ls[++i], sep, ls[++i], etc...
|
||||
#define S1 sep, ls[++i]
|
||||
#define S4 S1, S1, S1, S1
|
||||
#define S16 S4, S4, S4, S4
|
||||
#define S64 S16, S16, S16, S16
|
||||
|
||||
. = "[ls[++i]]" // Make sure the initial element is converted to text.
|
||||
|
||||
// Having the small concatenations come before the large ones boosted speed by an average of at least 5%.
|
||||
if (l-1 & 0x01) // 'i' will always be 1 here.
|
||||
. = text("[][][]", ., 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)
|
||||
. = text("[][][][][][][][][]", ., S4) // And so on....
|
||||
if (l-i & 0x08)
|
||||
. = text("[][][][][][][][][][][][][][][][][]", ., S4, S4)
|
||||
if (l-i & 0x10)
|
||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16)
|
||||
if (l-i & 0x20)
|
||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16, S16)
|
||||
if (l-i & 0x40)
|
||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64)
|
||||
while (l > i) // Chomp through the rest of the list, 128 elements at a time.
|
||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64)
|
||||
|
||||
#undef S64
|
||||
#undef S16
|
||||
#undef S4
|
||||
#undef S1
|
||||
else
|
||||
// Macros expand to long argument lists like so: ls[++i], ls[++i], ls[++i], etc...
|
||||
#define S1 ls[++i]
|
||||
#define S4 S1, S1, S1, S1
|
||||
#define S16 S4, S4, S4, S4
|
||||
#define S64 S16, S16, S16, S16
|
||||
|
||||
. = "[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.
|
||||
if (l-i & 0x02)
|
||||
. = text("[][][]", ., S1, S1) // Append 2 elements if the remaining elements are not a multiple of 4.
|
||||
if (l-i & 0x04)
|
||||
. = text("[][][][][]", ., S4) // And so on...
|
||||
if (l-i & 0x08)
|
||||
. = text("[][][][][][][][][]", ., S4, S4)
|
||||
if (l-i & 0x10)
|
||||
. = text("[][][][][][][][][][][][][][][][][]", ., S16)
|
||||
if (l-i & 0x20)
|
||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S16, S16)
|
||||
if (l-i & 0x40)
|
||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64)
|
||||
while (l > i) // Chomp through the rest of the list, 128 elements at a time.
|
||||
. = text("[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]\
|
||||
[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]", ., S64, S64)
|
||||
|
||||
#undef S64
|
||||
#undef S16
|
||||
#undef S4
|
||||
#undef S1
|
||||
|
||||
// Converts a string into a list by splitting the string at each delimiter found. (discarding the seperator)
|
||||
/proc/text2list(text, delimiter="\n")
|
||||
var/delim_len = length(delimiter)
|
||||
if (delim_len < 1)
|
||||
return list(text)
|
||||
|
||||
. = list()
|
||||
var/last_found = 1
|
||||
var/found
|
||||
|
||||
do
|
||||
found = findtext(text, delimiter, last_found, 0)
|
||||
. += copytext(text, last_found, found)
|
||||
last_found = found + delim_len
|
||||
while (found)
|
||||
@@ -1,4 +1,4 @@
|
||||
/proc/make_types_fancy(var/list/types)
|
||||
/proc/make_types_fancy(list/types)
|
||||
if (ispath(types))
|
||||
types = list(types)
|
||||
. = list()
|
||||
@@ -8,10 +8,10 @@
|
||||
/obj/effect/debris/cleanable = "CLEANABLE",
|
||||
/obj/item/radio/headset = "HEADSET",
|
||||
/obj/item/clothing/head/helmet/space = "SPESSHELMET",
|
||||
// /obj/item/book/manual = "MANUAL",
|
||||
// /obj/item/reagent_containers/food/drinks = "DRINK", //longest paths comes first
|
||||
// /obj/item/reagent_containers/food = "FOOD",
|
||||
// /obj/item/reagent_containers = "REAGENT_CONTAINERS",
|
||||
// /obj/item/book/manual = "MANUAL",
|
||||
// /obj/item/reagent_containers/food/drinks = "DRINK", //longest paths comes first
|
||||
// /obj/item/reagent_containers/food = "FOOD",
|
||||
// /obj/item/reagent_containers = "REAGENT_CONTAINERS",
|
||||
/obj/machinery/atmospherics = "ATMOS_MACH",
|
||||
/obj/machinery/portable_atmospherics = "PORT_ATMOS",
|
||||
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack = "MECHA_MISSILE_RACK",
|
||||
@@ -22,13 +22,13 @@
|
||||
/obj/effect = "EFFECT",
|
||||
/obj = "O",
|
||||
/datum = "D",
|
||||
// /turf/open = "OPEN",
|
||||
// /turf/closed = "CLOSED",
|
||||
// /turf/open = "OPEN",
|
||||
// /turf/closed = "CLOSED",
|
||||
/turf = "T",
|
||||
/mob/living/carbon = "CARBON",
|
||||
/mob/living/simple_animal = "SIMPLE",
|
||||
/mob/living = "LIVING",
|
||||
/mob = "M"
|
||||
/mob = "M",
|
||||
)
|
||||
for (var/tn in TYPES_SHORTCUTS)
|
||||
if (copytext(typename,1, length("[tn]/")+1)=="[tn]/" /*findtextEx(typename,"[tn]/",1,2)*/ )
|
||||
|
||||
@@ -6,19 +6,6 @@
|
||||
/// Checks if all high bits in req_mask are set in bitfield.
|
||||
#define BIT_TEST_ALL(bitfield, req_mask) ((~(bitfield) & (req_mask)) == 0)
|
||||
|
||||
/// Supposedly the fastest way to do this according to https://gist.github.com/Giacom/be635398926bb463b42a
|
||||
#define RANGE_TURFS(RADIUS, CENTER) \
|
||||
block( \
|
||||
locate(max(CENTER.x-(RADIUS),1), max(CENTER.y-(RADIUS),1), CENTER.z), \
|
||||
locate(min(CENTER.x+(RADIUS),world.maxx), min(CENTER.y+(RADIUS),world.maxy), CENTER.z) \
|
||||
)
|
||||
|
||||
#define RANGE_TURFS_OR_EMPTY(RADIUS, CENTER) \
|
||||
(CENTER? block( \
|
||||
locate(max(CENTER.x-(RADIUS),1), max(CENTER.y-(RADIUS),1), CENTER.z), \
|
||||
locate(min(CENTER.x+(RADIUS),world.maxx), min(CENTER.y+(RADIUS),world.maxy), CENTER.z) \
|
||||
) : list())
|
||||
|
||||
/// Inverts the colour of an HTML string.
|
||||
/proc/invertHTML(HTMLstring)
|
||||
if (!(istext(HTMLstring)))
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
///Gets all contents of contents and returns them all in a list.
|
||||
/atom/proc/GetAllContents(var/T)
|
||||
/**
|
||||
* Gets all contents of contents and returns them all in a list.
|
||||
*/
|
||||
/atom/proc/get_all_contents(target)
|
||||
var/list/processing_list = list(src)
|
||||
var/i = 0
|
||||
var/lim = 1
|
||||
if(T)
|
||||
if(target)
|
||||
. = list()
|
||||
while(i < lim)
|
||||
var/atom/A = processing_list[++i]
|
||||
//Byond does not allow things to be in multiple contents, or double parent-child hierarchies, so only += is needed
|
||||
//This is also why we don't need to check against assembled as we go along
|
||||
/**
|
||||
* Byond does not allow things to be in multiple contents, or double parent-child hierarchies, so only += is needed.
|
||||
* This is also why we don't need to check against assembled as we go along.
|
||||
*/
|
||||
processing_list += A.contents
|
||||
lim = processing_list.len
|
||||
if(istype(A,T))
|
||||
if(istype(A, target))
|
||||
. += A
|
||||
else
|
||||
while(i < lim)
|
||||
@@ -20,9 +24,9 @@
|
||||
lim = processing_list.len
|
||||
return processing_list
|
||||
|
||||
/atom/proc/GetAllContentsIgnoring(list/ignore_typecache)
|
||||
/atom/proc/get_all_contents_ignoring(list/ignore_typecache)
|
||||
if(!length(ignore_typecache))
|
||||
return GetAllContents()
|
||||
return get_all_contents()
|
||||
var/list/processing = list(src)
|
||||
. = list()
|
||||
var/i = 0
|
||||
|
||||
15
code/__HELPERS/varset_callback.dm
Normal file
15
code/__HELPERS/varset_callback.dm
Normal file
@@ -0,0 +1,15 @@
|
||||
#define VARSET_LIST_CALLBACK(target, var_name, var_value) CALLBACK(GLOBAL_PROC, /proc/___callbackvarset, ##target, ##var_name, ##var_value)
|
||||
// Dupe code because dm can't handle 3 level deep macros.
|
||||
#define VARSET_CALLBACK(datum, var, var_value) CALLBACK(GLOBAL_PROC, /proc/___callbackvarset, ##datum, NAMEOF(##datum, ##var), ##var_value)
|
||||
// We'll see about those 3-level deep macros.
|
||||
#define VARSET_IN(datum, var, var_value, time) addtimer(VARSET_CALLBACK(datum, var, var_value), time)
|
||||
|
||||
/proc/___callbackvarset(list_or_datum, var_name, var_value)
|
||||
if(length(list_or_datum))
|
||||
list_or_datum[var_name] = var_value
|
||||
return
|
||||
var/datum/datum = list_or_datum
|
||||
if(IsAdminAdvancedProcCall())
|
||||
datum.vv_edit_var(var_name, var_value) //same result generally, unless badmemes
|
||||
else
|
||||
datum.vars[var_name] = var_value
|
||||
@@ -1,58 +1,63 @@
|
||||
/*
|
||||
plot_vector is a helper datum for plotting a path in a straight line towards a target turf.
|
||||
This datum converts from world space (turf.x and turf.y) to pixel space, which the datum keeps track of itself. This
|
||||
should work with any size turfs (i.e. 32x32, 64x64) as it references world.icon_size (note: not actually tested with
|
||||
anything other than 32x32 turfs).
|
||||
|
||||
setup()
|
||||
This should be called after creating a new instance of a plot_vector datum.
|
||||
This does the initial setup and calculations. Since we are travelling in a straight line we only need to calculate
|
||||
the vector and x/y steps once. x/y steps are capped to 1 full turf, whichever is further. If we are travelling along
|
||||
the y axis each step will be +/- 1 y, and the x movement reduced based on the angle (tangent calculation). After
|
||||
this every subsequent step will be incremented based on these calculations.
|
||||
Inputs:
|
||||
source - the turf the object is starting from
|
||||
target - the target turf the object is travelling towards
|
||||
xo - starting pixel_x offset, typically won't be needed, but included in case someone has a need for it later
|
||||
yo - same as xo, but for the y_pixel offset
|
||||
|
||||
increment()
|
||||
Adds the offset to the current location - incrementing it by one step along the vector.
|
||||
|
||||
return_angle()
|
||||
Returns the direction (angle in degrees) the object is travelling in.
|
||||
|
||||
(N)
|
||||
90<39>
|
||||
^
|
||||
|
|
||||
(W) 180<38> <--+--> 0<> (E)
|
||||
|
|
||||
v
|
||||
-90<39>
|
||||
(S)
|
||||
|
||||
return_hypotenuse()
|
||||
Returns the distance of travel for each step of the vector, relative to each full step of movement. 1 is a full turf
|
||||
length. Currently used as a multiplier for scaling effects that should be contiguous, like laser beams.
|
||||
|
||||
return_location()
|
||||
Returns a vector_loc datum containing the current location data of the object (see /datum/vector_loc). This includes
|
||||
the turf it currently should be at, as well as the pixel offset from the centre of that turf. Typically increment()
|
||||
would be called before this if you are going to move an object based on it's vector data.
|
||||
*/
|
||||
/**
|
||||
* plot_vector is a helper datum for plotting a path in a straight line towards a target turf.
|
||||
* This datum converts from world space (turf.x and turf.y) to pixel space, which the datum keeps track of itself. This
|
||||
* should work with any size turfs (i.e. 32x32, 64x64) as it references world.icon_size (note: not actually tested with
|
||||
* anything other than 32x32 turfs).
|
||||
*
|
||||
* setup()
|
||||
* This should be called after creating a new instance of a plot_vector datum.
|
||||
* This does the initial setup and calculations. Since we are travelling in a straight line we only need to calculate
|
||||
* the vector and x/y steps once. x/y steps are capped to 1 full turf, whichever is further. If we are travelling along
|
||||
* the y axis each step will be +/- 1 y, and the x movement reduced based on the angle (tangent calculation). After
|
||||
* this every subsequent step will be incremented based on these calculations.
|
||||
* Inputs:
|
||||
* source - the turf the object is starting from
|
||||
* target - the target turf the object is travelling towards
|
||||
* xo - starting pixel_x offset, typically won't be needed, but included in case someone has a need for it later
|
||||
* yo - same as xo, but for the y_pixel offset
|
||||
*
|
||||
* increment()
|
||||
* Adds the offset to the current location - incrementing it by one step along the vector.
|
||||
*
|
||||
* return_angle()
|
||||
* Returns the direction (angle in degrees) the object is travelling in.
|
||||
*
|
||||
* (N)
|
||||
* 90<39>
|
||||
* ^
|
||||
* |
|
||||
* (W) 180<38> <--+--> 0<> (E)
|
||||
* |
|
||||
* v
|
||||
* -90<39>
|
||||
* (S)
|
||||
*
|
||||
* return_hypotenuse()
|
||||
* Returns the distance of travel for each step of the vector, relative to each full step of movement. 1 is a full turf
|
||||
* length. Currently used as a multiplier for scaling effects that should be contiguous, like laser beams.
|
||||
*
|
||||
* return_location()
|
||||
* Returns a vector_loc datum containing the current location data of the object (see /datum/vector_loc). This includes
|
||||
* the turf it currently should be at, as well as the pixel offset from the centre of that turf. Typically increment()
|
||||
* would be called before this if you are going to move an object based on it's vector data.
|
||||
*/
|
||||
|
||||
/datum/plot_vector
|
||||
var/turf/source
|
||||
var/turf/target
|
||||
var/angle = 0 // direction of travel in degrees
|
||||
var/loc_x = 0 // in pixels from the left edge of the map
|
||||
var/loc_y = 0 // in pixels from the bottom edge of the map
|
||||
var/loc_z = 0 // loc z is in world space coordinates (i.e. z level) - we don't care about measuring pixels for this
|
||||
var/offset_x = 0 // distance to increment each step
|
||||
/// Direction of travel in degrees.
|
||||
var/angle = 0
|
||||
/// In pixels from the left edge of the map.
|
||||
var/loc_x = 0
|
||||
/// In pixels from the bottom edge of the map.
|
||||
var/loc_y = 0
|
||||
/// loc z is in world space coordinates (i.e. z level) - we don't care about measuring pixels for this.
|
||||
var/loc_z = 0
|
||||
/// Distance to increment each step.
|
||||
var/offset_x = 0
|
||||
var/offset_y = 0
|
||||
|
||||
/datum/plot_vector/proc/setup(var/turf/S, var/turf/T, var/xo = 0, var/yo = 0, var/angle_offset=0)
|
||||
/datum/plot_vector/proc/setup(turf/S, turf/T, xo = 0, yo = 0, angle_offset=0)
|
||||
source = S
|
||||
target = T
|
||||
|
||||
@@ -115,7 +120,7 @@ return_location()
|
||||
/datum/plot_vector/proc/return_hypotenuse()
|
||||
return sqrt(((offset_x / 32) ** 2) + ((offset_y / 32) ** 2))
|
||||
|
||||
/datum/plot_vector/proc/return_location(var/datum/vector_loc/data)
|
||||
/datum/plot_vector/proc/return_location(datum/vector_loc/data)
|
||||
if(!data)
|
||||
data = new()
|
||||
data.loc = locate(round(loc_x / world.icon_size, 1), round(loc_y / world.icon_size, 1), loc_z)
|
||||
@@ -125,13 +130,13 @@ return_location()
|
||||
data.pixel_y = loc_y - (data.loc.y * world.icon_size)
|
||||
return data
|
||||
|
||||
/*
|
||||
vector_loc is a helper datum for returning precise location data from plot_vector. It includes the turf the object is in
|
||||
as well as the pixel offsets.
|
||||
|
||||
return_turf()
|
||||
Returns the turf the object should be currently located in.
|
||||
*/
|
||||
/**
|
||||
* vector_loc is a helper datum for returning precise location data from plot_vector. It includes the turf the object is in
|
||||
* as well as the pixel offsets.
|
||||
*
|
||||
* return_turf()
|
||||
* Returns the turf the object should be currently located in.
|
||||
*/
|
||||
/datum/vector_loc
|
||||
var/turf/loc
|
||||
var/pixel_x
|
||||
|
||||
@@ -9,7 +9,13 @@
|
||||
)
|
||||
MA.vis_contents.len = 0 // y ea let's not copy those
|
||||
MA.alpha = alpha
|
||||
MA.color = rgba_construct_color_matrix(1, 0, 0, -1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 0, 1, r, g, b, 0)
|
||||
MA.color = rgba_construct_color_matrix(
|
||||
1, 0, 0, -1,
|
||||
0, 1, 0, -1,
|
||||
0, 0, 1, -1,
|
||||
0, 0, 0, 1,
|
||||
r, g, b, 0,
|
||||
)
|
||||
MA.appearance_flags = RESET_TRANSFORM | RESET_COLOR
|
||||
MA.plane = FLOAT_PLANE
|
||||
MA.layer = FLOAT_LAYER
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
* color should be rgb or rgba, or matrices
|
||||
*/
|
||||
/proc/animate_color_shift(atom/A, color1 = "#ffffff", color2 = "#000000", time1 = 1 SECONDS, time2 = 1 SECONDS, easing = LINEAR_EASING, loop = -1)
|
||||
var/c1
|
||||
var/c2
|
||||
var/original_color
|
||||
var/target_color
|
||||
if(!islist(color1))
|
||||
c1 = color1
|
||||
c2 = color2
|
||||
original_color = color1
|
||||
target_color = color2
|
||||
else
|
||||
// assume matrix
|
||||
c1 = color1
|
||||
c2 = color2
|
||||
animate(A, color = c1, easing = easing, time = 1080 * 0.5, loop = -1)
|
||||
animate(A, color = c2, easing = easing, time = 1080 * 0.5)
|
||||
original_color = color1
|
||||
target_color = color2
|
||||
animate(A, color = original_color, easing = easing, time = 1080 * 0.5, loop = -1)
|
||||
animate(A, color = target_color, easing = easing, time = 1080 * 0.5)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/**
|
||||
* gives an atom the ability to be seen anywhere
|
||||
* Gives an atom the ability to be seen anywhere.
|
||||
*/
|
||||
/atom/proc/vfx_make_see_anywhere()
|
||||
__vfx_apply_see_anywhere_overlay()
|
||||
appearance_flags &= ~TILE_BOUND
|
||||
|
||||
/**
|
||||
* removes the ability for an atom to be seen anywhere
|
||||
* Removes the ability for an atom to be seen anywhere.
|
||||
*/
|
||||
/atom/proc/vfx_remove_see_anywhere()
|
||||
__vfx_remove_see_anywhere_overlay()
|
||||
@@ -40,13 +40,13 @@ GLOBAL_DATUM_INIT(see_anywhere_appearance, /mutable_appearance, init_see_anywher
|
||||
return I
|
||||
|
||||
/**
|
||||
* just applies the see anywhere overlay to the atom.
|
||||
* Just applies the see anywhere overlay to the atom.
|
||||
*/
|
||||
/atom/proc/__vfx_apply_see_anywhere_overlay()
|
||||
add_overlay(GLOB.see_anywhere_appearance)
|
||||
|
||||
/**
|
||||
* just removes the see anywhere overlay to the atom
|
||||
* Just removes the see anywhere overlay to the atom.
|
||||
*/
|
||||
/atom/proc/__vfx_remove_see_anywhere_overlay()
|
||||
cut_overlay(GLOB.see_anywhere_appearance)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* shakes the camera of any client watching from an atom's perspective
|
||||
* Shakes the camera of any client watching from an atom's perspective.
|
||||
*/
|
||||
/proc/shake_camera(atom/movable/AM, duration, strength = 1)
|
||||
if(!AM || isEye(AM) || isAI(AM))
|
||||
|
||||
@@ -1,23 +1,31 @@
|
||||
//This is intended to be a full wrapper. DO NOT directly modify its values
|
||||
///Container for client viewsize
|
||||
/**
|
||||
* This is intended to be a full wrapper. DO NOT directly modify its values.
|
||||
* Container for client viewsize.
|
||||
*/
|
||||
/datum/view_data
|
||||
/// Width offset to apply to the default view string if we're not supressed for some reason
|
||||
/// Width offset to apply to the default view string if we're not supressed for some reason.
|
||||
var/width = 0
|
||||
/// Height offset to apply to the default view string, see above
|
||||
/// Height offset to apply to the default view string, see above.
|
||||
var/height = 0
|
||||
/// This client's current "default" view, in the format "WidthxHeight"
|
||||
/// We add/remove from this when we want to change their window size
|
||||
/**
|
||||
* This client's current "default" view, in the format "WidthxHeight".
|
||||
* We add/remove from this when we want to change their window size.
|
||||
*/
|
||||
var/default = ""
|
||||
/// This client's current zoom level, if it's not being supressed
|
||||
/// If it's 0, we autoscale to the size of the window. Otherwise it's treated as the ratio between
|
||||
/// the pixels on the map and output pixels. Only looks proper nice in increments of whole numbers (iirc)
|
||||
/// Stored here so other parts of the code have a non blocking way of getting a user's functional zoom
|
||||
/**
|
||||
* This client's current zoom level, if it's not being supressed.
|
||||
* If it's 0, we autoscale to the size of the window. Otherwise it's treated as the ratio between
|
||||
* the pixels on the map and output pixels. Only looks proper nice in increments of whole numbers (iirc).
|
||||
* Stored here so other parts of the code have a non blocking way of getting a user's functional zoom.
|
||||
*/
|
||||
var/zoom = 0
|
||||
/// If the view is currently being supressed by some other "monitor"
|
||||
/// For when you want to own the client's eye without fucking with their viewport
|
||||
/// Doesn't make sense for a binocoler to effect your view in a camera console
|
||||
/**
|
||||
* If the view is currently being supressed by some other "monitor".
|
||||
* For when you want to own the client's eye without fucking with their viewport.
|
||||
* Doesn't make sense for a binocoler to effect your view in a camera console.
|
||||
*/
|
||||
var/is_suppressed = FALSE
|
||||
/// The client that owns this view packet
|
||||
/// The client that owns this view packet.
|
||||
var/client/chief = null
|
||||
|
||||
/datum/view_data/New(client/owner, view_string)
|
||||
@@ -35,11 +43,13 @@
|
||||
return
|
||||
resetFormat()
|
||||
|
||||
/datum/view_data/proc/assertFormat() // T-Pose
|
||||
/// T-Pose
|
||||
/datum/view_data/proc/assertFormat()
|
||||
// winset(chief, "mapwindow.map", "zoom=0")
|
||||
// We're using icon dropdown instead
|
||||
|
||||
/datum/view_data/proc/resetFormat()//Cuck
|
||||
/// Cuck
|
||||
/datum/view_data/proc/resetFormat()
|
||||
// winset(chief, "mapwindow.map", "zoom=[chief.prefs.pixel_size]")
|
||||
// We're using icon dropdown instead
|
||||
|
||||
@@ -67,9 +77,14 @@
|
||||
apply()
|
||||
|
||||
/datum/view_data/proc/setTo(toAdd)
|
||||
var/list/shitcode = getviewsize(toAdd) // Backward compatability to account
|
||||
width = shitcode[1] // For a change in how sizes get calculated. we used to include world.view in
|
||||
height = shitcode[2] // This, but it was jank, so I had to move it
|
||||
/**
|
||||
* Backward compatability to account.
|
||||
* For a change in how sizes get calculated. we used to include world.view in
|
||||
* This, but it was jank, so I had to move it.
|
||||
*/
|
||||
var/list/shitcode = getviewsize(toAdd)
|
||||
width = shitcode[1]
|
||||
height = shitcode[2]
|
||||
apply()
|
||||
|
||||
/datum/view_data/proc/setBoth(wid, hei)
|
||||
|
||||
45
code/_globalvars/lists/names.dm
Normal file
45
code/_globalvars/lists/names.dm
Normal file
@@ -0,0 +1,45 @@
|
||||
//! loaded on startup because of "
|
||||
//! would include in rsc if ' was used
|
||||
|
||||
GLOBAL_LIST_INIT(ai_names, world.file2list("strings/names/ai.txt"))
|
||||
// GLOBAL_LIST_INIT(carp_names, world.file2list("strings/names/carp.txt"))
|
||||
GLOBAL_LIST_INIT(clown_names, world.file2list("strings/names/clown.txt"))
|
||||
// GLOBAL_LIST_INIT(clown_names, world.file2list("strings/names/clown.txt"))
|
||||
GLOBAL_LIST_INIT(commando_names, world.file2list("strings/names/death_commando.txt"))
|
||||
// GLOBAL_LIST_INIT(ethereal_names, world.file2list("strings/names/ethereal.txt"))
|
||||
GLOBAL_LIST_INIT(first_names_female, world.file2list("strings/names/first_female.txt"))
|
||||
GLOBAL_LIST_INIT(first_names_male, world.file2list("strings/names/first_male.txt"))
|
||||
// GLOBAL_LIST_INIT(first_names, world.file2list("strings/names/first.txt"))
|
||||
// GLOBAL_LIST_INIT(golem_names, world.file2list("strings/names/golem.txt"))
|
||||
GLOBAL_LIST_INIT(last_names, world.file2list("strings/names/last.txt"))
|
||||
// GLOBAL_LIST_INIT(lizard_names_female, world.file2list("strings/names/lizard_female.txt"))
|
||||
// GLOBAL_LIST_INIT(lizard_names_female, world.file2list("strings/names/lizard_female.txt"))
|
||||
// GLOBAL_LIST_INIT(lizard_names_male, world.file2list("strings/names/lizard_male.txt"))
|
||||
// GLOBAL_LIST_INIT(lizard_names_male, world.file2list("strings/names/lizard_male.txt"))
|
||||
// GLOBAL_LIST_INIT(megacarp_first_names, world.file2list("strings/names/megacarp1.txt"))
|
||||
// GLOBAL_LIST_INIT(megacarp_last_names, world.file2list("strings/names/megacarp2.txt"))
|
||||
// GLOBAL_LIST_INIT(mime_names, world.file2list("strings/names/mime.txt"))
|
||||
// GLOBAL_LIST_INIT(moth_first, world.file2list("strings/names/moth_first.txt"))
|
||||
// GLOBAL_LIST_INIT(moth_last, world.file2list("strings/names/moth_last.txt"))
|
||||
// GLOBAL_LIST_INIT(nightmare_names, world.file2list("strings/names/nightmare.txt"))
|
||||
GLOBAL_LIST_INIT(ninja_names, world.file2list("strings/names/ninjaname.txt"))
|
||||
GLOBAL_LIST_INIT(ninja_titles, world.file2list("strings/names/ninjatitle.txt"))
|
||||
// GLOBAL_LIST_INIT(plasmaman_names, world.file2list("strings/names/plasmaman.txt"))
|
||||
// GLOBAL_LIST_INIT(posibrain_names, world.file2list("strings/names/posibrain.txt"))
|
||||
// GLOBAL_LIST_INIT(religion_names, world.file2list("strings/names/religion.txt"))
|
||||
GLOBAL_LIST_INIT(wizard_first, world.file2list("strings/names/wizardfirst.txt"))
|
||||
GLOBAL_LIST_INIT(wizard_second, world.file2list("strings/names/wizardsecond.txt"))
|
||||
|
||||
GLOBAL_LIST_INIT(skrell_first_names, world.file2list("strings/names/skrell_first.txt"))
|
||||
GLOBAL_LIST_INIT(skrell_last_names, world.file2list("strings/names/skrell_last.txt"))
|
||||
|
||||
GLOBAL_LIST_INIT(adjectives, world.file2list("strings/names/adjectives.txt"))
|
||||
// GLOBAL_LIST_INIT(adverbs, world.file2list("strings/names/adverbs.txt"))
|
||||
// GLOBAL_LIST_INIT(dream_strings, world.file2list("strings/dreamstrings.txt"))
|
||||
// GLOBAL_LIST_INIT(greek_alphabet, world.file2list("strings/greek_alphabet.txt"))
|
||||
// GLOBAL_LIST_INIT(gross_adjectives, world.file2list("strings/names/gross_adjectives.txt"))
|
||||
// GLOBAL_LIST_INIT(hive_names, world.file2list("strings/names/hive_names.txt"))
|
||||
// GLOBAL_LIST_INIT(ing_verbs, world.file2list("strings/names/ing_verbs.txt"))
|
||||
// GLOBAL_LIST_INIT(martial_prefix, world.file2list("strings/names/martial_prefix.txt"))
|
||||
// GLOBAL_LIST_INIT(vampire_house_names, world.file2list("strings/names/vampire_house_names.txt"))
|
||||
GLOBAL_LIST_INIT(verbs, world.file2list("strings/names/verbs.txt"))
|
||||
@@ -80,7 +80,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
qdel(Master)
|
||||
else
|
||||
var/list/subsytem_types = subtypesof(/datum/controller/subsystem)
|
||||
sortTim(subsytem_types, /proc/cmp_subsystem_init)
|
||||
tim_sort(subsytem_types, /proc/cmp_subsystem_init)
|
||||
for(var/I in subsytem_types)
|
||||
_subsystems += new I
|
||||
Master = src
|
||||
@@ -95,7 +95,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
|
||||
/datum/controller/master/Shutdown()
|
||||
processing = FALSE
|
||||
sortTim(subsystems, /proc/cmp_subsystem_init)
|
||||
tim_sort(subsystems, /proc/cmp_subsystem_init)
|
||||
reverseRange(subsystems)
|
||||
for(var/datum/controller/subsystem/ss in subsystems)
|
||||
log_world("Shutting down [ss.name] subsystem...")
|
||||
@@ -181,7 +181,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
to_chat(world, "<span class='boldannounce'>Initializing subsystems...</span>")
|
||||
|
||||
// Sort subsystems by init_order, so they initialize in the correct order.
|
||||
sortTim(subsystems, /proc/cmp_subsystem_init)
|
||||
tim_sort(subsystems, /proc/cmp_subsystem_init)
|
||||
|
||||
var/start_timeofday = REALTIMEOFDAY
|
||||
// Initialize subsystems.
|
||||
@@ -202,7 +202,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
SetRunLevel(RUNLEVEL_LOBBY)
|
||||
|
||||
// Sort subsystems by display setting for easy access.
|
||||
sortTim(subsystems, /proc/cmp_subsystem_display)
|
||||
tim_sort(subsystems, /proc/cmp_subsystem_display)
|
||||
|
||||
// Set world options.
|
||||
|
||||
@@ -287,9 +287,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
queue_tail = null
|
||||
//these sort by lower priorities first to reduce the number of loops needed to add subsequent SS's to the queue
|
||||
//(higher subsystems will be sooner in the queue, adding them later in the loop means we don't have to loop thru them next queue add)
|
||||
sortTim(SStickersubsystems, /proc/cmp_subsystem_priority)
|
||||
tim_sort(SStickersubsystems, /proc/cmp_subsystem_priority)
|
||||
for(var/I in runlevel_sorted_subsystems)
|
||||
sortTim(runlevel_sorted_subsystems, /proc/cmp_subsystem_priority)
|
||||
tim_sort(runlevel_sorted_subsystems, /proc/cmp_subsystem_priority)
|
||||
I += SStickersubsystems
|
||||
|
||||
var/cached_runlevel = current_runlevel
|
||||
|
||||
@@ -146,7 +146,7 @@ SUBSYSTEM_DEF(events)
|
||||
else
|
||||
qdel(holiday)
|
||||
|
||||
sortTim(holidays, /proc/cmp_holiday_priority)
|
||||
tim_sort(holidays, /proc/cmp_holiday_priority)
|
||||
// // regenerate station name because holiday prefixes.
|
||||
// set_station_name(new_station_name())
|
||||
// world.update_status()
|
||||
|
||||
@@ -63,7 +63,7 @@ SUBSYSTEM_DEF(garbage)
|
||||
var/list/dellog = list()
|
||||
|
||||
//sort by how long it's wasted hard deleting
|
||||
sortTim(items, cmp=/proc/cmp_qdel_item_time, associative = TRUE)
|
||||
tim_sort(items, cmp=/proc/cmp_qdel_item_time, associative = TRUE)
|
||||
for(var/path in items)
|
||||
var/datum/qdel_item/I = items[path]
|
||||
dellog += "Path: [path]"
|
||||
@@ -337,7 +337,7 @@ SUBSYSTEM_DEF(garbage)
|
||||
++c
|
||||
var/list/built = list("counted [c] datums in [round((world.timeofday - start) * 0.1, 0.01)] seconds")
|
||||
start = world.timeofday
|
||||
sortTim(L, /proc/cmp_numeric_dsc, associative = TRUE)
|
||||
tim_sort(L, /proc/cmp_numeric_dsc, associative = TRUE)
|
||||
built += "sorted [c] datums in [round((world.timeofday - start) * 0.1, 0.01)] seconds"
|
||||
for(var/i in L)
|
||||
built += "[i] - [L[i]]"
|
||||
@@ -423,7 +423,7 @@ SUBSYSTEM_DEF(garbage)
|
||||
var/list/built = list("counted [c] datums in [round((world.timeofday - start) * 0.1, 0.01)] seconds")
|
||||
start = world.timeofday
|
||||
built += "sorted [c] datums in [round((world.timeofday - start) * 0.1, 0.01)] seconds"
|
||||
sortTim(L, /proc/cmp_numeric_dsc, associative = TRUE)
|
||||
tim_sort(L, /proc/cmp_numeric_dsc, associative = TRUE)
|
||||
for(var/i in L)
|
||||
built += "[i] - [L[i]]"
|
||||
var/datum/browser/B = new(usr, "datum_outgoing_ref_count", "datum outgoing ref count")
|
||||
|
||||
@@ -39,11 +39,11 @@ SUBSYSTEM_DEF(job)
|
||||
if(LAZYLEN(job.departments))
|
||||
add_to_departments(job)
|
||||
|
||||
sortTim(occupations, /proc/cmp_job_datums)
|
||||
tim_sort(occupations, /proc/cmp_job_datums)
|
||||
for(var/D in department_datums)
|
||||
var/datum/department/dept = department_datums[D]
|
||||
sortTim(dept.jobs, /proc/cmp_job_datums, TRUE)
|
||||
sortTim(dept.primary_jobs, /proc/cmp_job_datums, TRUE)
|
||||
tim_sort(dept.jobs, /proc/cmp_job_datums, TRUE)
|
||||
tim_sort(dept.primary_jobs, /proc/cmp_job_datums, TRUE)
|
||||
|
||||
return TRUE
|
||||
|
||||
@@ -71,7 +71,7 @@ SUBSYSTEM_DEF(job)
|
||||
var/datum/department/D = new t()
|
||||
department_datums[D.name] = D
|
||||
|
||||
sortTim(department_datums, /proc/cmp_department_datums, TRUE)
|
||||
tim_sort(department_datums, /proc/cmp_department_datums, TRUE)
|
||||
|
||||
/datum/controller/subsystem/job/proc/get_all_department_datums()
|
||||
var/list/dept_datums = list()
|
||||
|
||||
@@ -23,7 +23,7 @@ var/global/datum/controller/occupations/job_master
|
||||
continue
|
||||
job = new J
|
||||
occupations += job
|
||||
sortTim(occupations, /proc/cmp_job_datums)
|
||||
tim_sort(occupations, /proc/cmp_job_datums)
|
||||
return TRUE
|
||||
|
||||
|
||||
|
||||
@@ -280,7 +280,7 @@ SUBSYSTEM_DEF(timer)
|
||||
return
|
||||
|
||||
// Sort all timers by time to run
|
||||
sortTim(alltimers, .proc/cmp_timer)
|
||||
tim_sort(alltimers, .proc/cmp_timer)
|
||||
|
||||
// Get the earliest timer, and if the TTR is earlier than the current world.time,
|
||||
// then set the head offset appropriately to be the earliest time tracked by the
|
||||
|
||||
@@ -168,7 +168,7 @@ SUBSYSTEM_DEF(transcore)
|
||||
/datum/controller/subsystem/transcore/proc/add_backup(datum/transhuman/mind_record/MR)
|
||||
ASSERT(MR)
|
||||
backed_up[MR.mindname] = MR
|
||||
backed_up = sortTim(backed_up, /proc/cmp_text_asc, TRUE)
|
||||
backed_up = tim_sort(backed_up, /proc/cmp_text_asc, TRUE)
|
||||
subsystem_log("Added [MR.mindname] to transcore DB.")
|
||||
|
||||
// Remove a mind_record from the backup-checking list. Keeps track of it in has_left // Why do we do that? ~Leshana
|
||||
@@ -183,7 +183,7 @@ SUBSYSTEM_DEF(transcore)
|
||||
/datum/controller/subsystem/transcore/proc/add_body(datum/transhuman/body_record/BR)
|
||||
ASSERT(BR)
|
||||
body_scans[BR.mydna.name] = BR
|
||||
body_scans = sortTim(body_scans, /proc/cmp_text_asc, TRUE)
|
||||
body_scans = tim_sort(body_scans, /proc/cmp_text_asc, TRUE)
|
||||
subsystem_log("Added [BR.mydna.name] to transcore body DB.")
|
||||
|
||||
// Remove a body record from the database (Usually done when someone cryos) // Why? ~Leshana
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
/*
|
||||
Exonet Protocol Version 2
|
||||
|
||||
This is designed to be a fairly simple fake-networking system, allowing you to send and receive messages
|
||||
between the exonet_protocol datums, and for atoms to react to those messages, based on the contents of the message.
|
||||
Hopefully, this can evolve to be a more robust fake-networking system and allow for some devious network hacking in the future.
|
||||
|
||||
Version 1 never existed.
|
||||
|
||||
*Setting up*
|
||||
|
||||
To set up the exonet link, define a variable on your desired atom it is like this;
|
||||
var/datum/exonet_protocol/exonet = null
|
||||
Afterwards, before you want to do networking, call exonet = New(src), then exonet.make_address(string), and give it a string to hash into the new IP.
|
||||
The reason it needs a string is so you can have the addresses be persistant, assuming no-one already took it first.
|
||||
|
||||
When you're no longer wanting to use the address and want to free it up, like when you want to Destroy() it, you need to call remove_address()
|
||||
|
||||
*Sending messages*
|
||||
|
||||
To send a message to another datum, you need to know it's EPv2 (fake IP) address. Once you know that, call send_message(), place your
|
||||
intended address in the first argument, then the message in the second. For example, send_message(exonet.address, "ping") will make you
|
||||
ping yourself.
|
||||
|
||||
*Receiving messages*
|
||||
You don't need to do anything special to receive the messages, other than give your target exonet datum an address as well. Once something hits
|
||||
your datum with send_message(), receive_message() is called, and the default action is to call receive_exonet_message() on the datum's holder.
|
||||
You'll want to override receive_exonet_message() on your atom, and define what will occur when the message is received.
|
||||
The receiving atom will receive the origin atom (the atom that sent the message), the origin address, and finally the message itself.
|
||||
It's suggested to start with an if or switch statement for the message, to determine what to do.
|
||||
*/
|
||||
/**
|
||||
*! Exonet Protocol Version 2
|
||||
*
|
||||
* This is designed to be a fairly simple fake-networking system, allowing you to send and receive messages
|
||||
* between the exonet_protocol datums, and for atoms to react to those messages, based on the contents of the message.
|
||||
* Hopefully, this can evolve to be a more robust fake-networking system and allow for some devious network hacking in the future.
|
||||
*
|
||||
* Version 1 never existed.
|
||||
*
|
||||
* *Setting up*
|
||||
*
|
||||
* To set up the exonet link, define a variable on your desired atom it is like this;
|
||||
* var/datum/exonet_protocol/exonet = null
|
||||
* Afterwards, before you want to do networking, call exonet = New(src), then exonet.make_address(string), and give it a string to hash into the new IP.
|
||||
* The reason it needs a string is so you can have the addresses be persistant, assuming no-one already took it first.
|
||||
*
|
||||
* When you're no longer wanting to use the address and want to free it up, like when you want to Destroy() it, you need to call remove_address()
|
||||
*
|
||||
* *Sending messages*
|
||||
*
|
||||
* To send a message to another datum, you need to know it's EPv2 (fake IP) address. Once you know that, call send_message(), place your
|
||||
* intended address in the first argument, then the message in the second. For example, send_message(exonet.address, "ping") will make you
|
||||
* ping yourself.
|
||||
*
|
||||
* *Receiving messages*
|
||||
* You don't need to do anything special to receive the messages, other than give your target exonet datum an address as well. Once something hits
|
||||
* your datum with send_message(), receive_message() is called, and the default action is to call receive_exonet_message() on the datum's holder.
|
||||
* You'll want to override receive_exonet_message() on your atom, and define what will occur when the message is received.
|
||||
* The receiving atom will receive the origin atom (the atom that sent the message), the origin address, and finally the message itself.
|
||||
* It's suggested to start with an if or switch statement for the message, to determine what to do.
|
||||
*/
|
||||
|
||||
var/global/list/all_exonet_connections = list()
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
category = new category(src)
|
||||
categories += category
|
||||
categories_by_name[category.name] = category
|
||||
categories = sortTim(categories, /proc/cmp_auto_compare)
|
||||
categories = tim_sort(categories, /proc/cmp_auto_compare)
|
||||
|
||||
/datum/category_collection/Destroy()
|
||||
for(var/category in categories)
|
||||
@@ -49,7 +49,7 @@
|
||||
|
||||
// For whatever reason dd_insertObjectList(items, item) doesn't insert in the correct order
|
||||
// If you change this, confirm that character setup doesn't become completely unordered.
|
||||
sortTim(items, /proc/cmp_auto_compare)
|
||||
tim_sort(items, /proc/cmp_auto_compare)
|
||||
|
||||
/datum/category_group/Destroy()
|
||||
for(var/item in items)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
var/atom/A = parent
|
||||
if(A == W) //don't put yourself into yourself.
|
||||
return
|
||||
var/list/obj/item/storage/backpack/holding/matching = typecache_filter_list(W.GetAllContents(), typecacheof(/obj/item/storage/backpack/holding))
|
||||
var/list/obj/item/storage/backpack/holding/matching = typecache_filter_list(W.get_all_contents(), typecacheof(/obj/item/storage/backpack/holding))
|
||||
matching -= A
|
||||
if(istype(W, /obj/item/storage/backpack/holding) || matching.len)
|
||||
var/safety = alert(user, "Doing this will have extremely dire consequences for the station and its crew. Be sure you know what you're doing.", "Put in [A.name]?", "Abort", "Proceed")
|
||||
|
||||
@@ -29,8 +29,7 @@
|
||||
*/
|
||||
/atom/proc/ConflictElementCount(id)
|
||||
. = 0
|
||||
for(var/i in GetAllContents())
|
||||
for(var/i in get_all_contents())
|
||||
var/atom/movable/AM = i
|
||||
if(SEND_SIGNAL(AM, COMSIG_CONFLICT_ELEMENT_CHECK, id) & ELEMENT_CONFLICT_FOUND)
|
||||
++.
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
if(initial(O.abstract_type) == path)
|
||||
continue
|
||||
. += new path
|
||||
sortTim(., /proc/cmp_name_asc)
|
||||
tim_sort(., /proc/cmp_name_asc)
|
||||
|
||||
/datum/outfit
|
||||
/// the outfit's name
|
||||
@@ -167,7 +167,7 @@
|
||||
pda.name = "PDA-[H.real_name] ([assignment])"
|
||||
if(H.client.prefs.ringtone) // if null we use the job default
|
||||
pda.ringtone = H.client.prefs.ringtone
|
||||
sortTim(GLOB.PDAs, /proc/cmp_name_asc)
|
||||
tim_sort(GLOB.PDAs, /proc/cmp_name_asc)
|
||||
return pda
|
||||
|
||||
/datum/outfit/dd_SortValue()
|
||||
|
||||
@@ -6,7 +6,7 @@ GLOBAL_REAL_VAR(PROFILE_SLEEPCHECK)
|
||||
GLOBAL_REAL_VAR(PROFILE_TIME)
|
||||
|
||||
/proc/profile_show(user, sort = /proc/cmp_profile_avg_time_dsc)
|
||||
sortTim(PROFILE_STORE, sort, TRUE)
|
||||
tim_sort(PROFILE_STORE, sort, TRUE)
|
||||
|
||||
var/list/lines = list()
|
||||
|
||||
|
||||
@@ -310,5 +310,5 @@
|
||||
else if (possible_recipes.len == 1)
|
||||
return possible_recipes[1]
|
||||
else //okay, let's select the most complicated recipe
|
||||
sortTim(possible_recipes, /proc/cmp_recipe_complexity_dsc)
|
||||
tim_sort(possible_recipes, /proc/cmp_recipe_complexity_dsc)
|
||||
return possible_recipes[1]
|
||||
|
||||
@@ -68,7 +68,7 @@ var/datum/antagonist/deathsquad/deathsquad
|
||||
else
|
||||
syndicate_commando_rank = pick("Lieutenant", "Captain", "Major")
|
||||
|
||||
var/syndicate_commando_name = pick(last_names)
|
||||
var/syndicate_commando_name = pick(GLOB.last_names)
|
||||
|
||||
var/datum/preferences/A = new() //Randomize appearance for the commando.
|
||||
A.randomize_appearance_and_body_for(player.current)
|
||||
|
||||
@@ -25,7 +25,7 @@ var/datum/antagonist/ninja/ninjas
|
||||
/datum/antagonist/ninja/attempt_random_spawn()
|
||||
if(config_legacy.ninjas_allowed) ..()
|
||||
|
||||
/datum/antagonist/ninja/create_objectives(var/datum/mind/ninja)
|
||||
/datum/antagonist/ninja/create_objectives(datum/mind/ninja)
|
||||
|
||||
if(!..())
|
||||
return
|
||||
@@ -80,7 +80,7 @@ var/datum/antagonist/ninja/ninjas
|
||||
ninja_objective.owner = ninja
|
||||
ninja.objectives += ninja_objective
|
||||
|
||||
/datum/antagonist/ninja/greet(var/datum/mind/player)
|
||||
/datum/antagonist/ninja/greet(datum/mind/player)
|
||||
|
||||
if(!..())
|
||||
return 0
|
||||
@@ -88,17 +88,17 @@ var/datum/antagonist/ninja/ninjas
|
||||
player.store_memory("<B>Directive:</B> <span class='danger'>[directive]</span><br>")
|
||||
to_chat(player, "<b>Remember your directive:</b> [directive].")
|
||||
|
||||
/datum/antagonist/ninja/update_antag_mob(var/datum/mind/player)
|
||||
/datum/antagonist/ninja/update_antag_mob(datum/mind/player)
|
||||
..()
|
||||
var/ninja_title = pick(ninja_titles)
|
||||
var/ninja_name = pick(ninja_names)
|
||||
var/ninja_title = pick(GLOB.ninja_titles)
|
||||
var/ninja_name = pick(GLOB.ninja_names)
|
||||
var/mob/living/carbon/human/H = player.current
|
||||
if(istype(H))
|
||||
H.real_name = "[ninja_title] [ninja_name]"
|
||||
H.name = H.real_name
|
||||
player.name = H.name
|
||||
|
||||
/datum/antagonist/ninja/equip(var/mob/living/carbon/human/player)
|
||||
/datum/antagonist/ninja/equip(mob/living/carbon/human/player)
|
||||
|
||||
if(!..())
|
||||
return 0
|
||||
|
||||
@@ -29,7 +29,7 @@ var/datum/antagonist/technomancer/technomancers
|
||||
/datum/antagonist/technomancer/update_antag_mob(var/datum/mind/technomancer)
|
||||
..()
|
||||
technomancer.store_memory("<B>Remember:</B> Do not forget to purchase the functions and equipment you need.")
|
||||
technomancer.current.real_name = "[pick(wizard_first)] [pick(wizard_second)]"
|
||||
technomancer.current.real_name = "[pick(GLOB.wizard_first)] [pick(GLOB.wizard_second)]"
|
||||
technomancer.current.name = technomancer.current.real_name
|
||||
|
||||
/datum/antagonist/technomancer/equip(var/mob/living/carbon/human/technomancer_mob)
|
||||
|
||||
@@ -21,7 +21,7 @@ var/datum/antagonist/wizard/wizards
|
||||
..()
|
||||
wizards = src
|
||||
|
||||
/datum/antagonist/wizard/create_objectives(var/datum/mind/wizard)
|
||||
/datum/antagonist/wizard/create_objectives(datum/mind/wizard)
|
||||
|
||||
if(!..())
|
||||
return
|
||||
@@ -64,13 +64,13 @@ var/datum/antagonist/wizard/wizards
|
||||
wizard.objectives |= hijack_objective
|
||||
return
|
||||
|
||||
/datum/antagonist/wizard/update_antag_mob(var/datum/mind/wizard)
|
||||
/datum/antagonist/wizard/update_antag_mob(datum/mind/wizard)
|
||||
..()
|
||||
wizard.store_memory("<B>Remember:</B> do not forget to prepare your spells.")
|
||||
wizard.current.real_name = "[pick(wizard_first)] [pick(wizard_second)]"
|
||||
wizard.current.real_name = "[pick(GLOB.wizard_first)] [pick(GLOB.wizard_second)]"
|
||||
wizard.current.name = wizard.current.real_name
|
||||
|
||||
/datum/antagonist/wizard/equip(var/mob/living/carbon/human/wizard_mob)
|
||||
/datum/antagonist/wizard/equip(mob/living/carbon/human/wizard_mob)
|
||||
|
||||
if(!..())
|
||||
return 0
|
||||
@@ -98,32 +98,40 @@ var/datum/antagonist/wizard/wizards
|
||||
break
|
||||
if(!survivor)
|
||||
feedback_set_details("round_end_result","loss - wizard killed")
|
||||
to_chat(world, "<span class='danger'><font size = 3>The [(current_antagonists.len>1)?"[role_text_plural] have":"[role_text] has"] been killed by the crew!</font></span>")
|
||||
to_chat(world, SPAN_ANNOUNCE("The [(current_antagonists.len>1)?"[role_text_plural] have":"[role_text] has"] been killed by the crew!"))
|
||||
|
||||
//To batch-remove wizard spells. Linked to mind.dm.
|
||||
/**
|
||||
* To batch-remove wizard spells. Linked to mind.dm.
|
||||
*/
|
||||
/mob/proc/spellremove()
|
||||
for(var/spell/spell_to_remove in src.spell_list)
|
||||
remove_spell(spell_to_remove)
|
||||
|
||||
// Does this clothing slot count as wizard garb? (Combines a few checks)
|
||||
/proc/is_wiz_garb(var/obj/item/clothing/C)
|
||||
return C && C.wizard_garb
|
||||
/**
|
||||
* Does this clothing slot count as wizard garb? (Combines a few checks)
|
||||
*/
|
||||
/proc/is_wiz_garb(obj/item/clothing/Clothing)
|
||||
return Clothing && Clothing.wizard_garb
|
||||
|
||||
/*Checks if the wizard is wearing the proper attire.
|
||||
Made a proc so this is not repeated 14 (or more) times.*/
|
||||
/**
|
||||
* Checks if the wizard is wearing the proper attire.
|
||||
* Made a proc so this is not repeated 14 (or more) times.
|
||||
*/
|
||||
/mob/proc/wearing_wiz_garb()
|
||||
to_chat(src, "Silly creature, you're not a human. Only humans can cast this spell.")
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
// Humans can wear clothes.
|
||||
/**
|
||||
* Humans can wear clothes.
|
||||
*/
|
||||
/mob/living/carbon/human/wearing_wiz_garb()
|
||||
if(!is_wiz_garb(src.wear_suit))
|
||||
to_chat(src, "<span class='warning'>I don't feel strong enough without my robe.</span>")
|
||||
return 0
|
||||
to_chat(src, SPAN_WARNING("I don't feel strong enough without my robe."))
|
||||
return FALSE
|
||||
if(!is_wiz_garb(src.shoes))
|
||||
to_chat(src, "<span class='warning'>I don't feel strong enough without my sandals.</span>")
|
||||
return 0
|
||||
to_chat(src, SPAN_WARNING("I don't feel strong enough without my sandals."))
|
||||
return FALSE
|
||||
if(!is_wiz_garb(src.head))
|
||||
to_chat(src, "<span class='warning'>I don't feel strong enough without my hat.</span>")
|
||||
return 0
|
||||
return 1
|
||||
to_chat(src, SPAN_WARNING("I don't feel strong enough without my hat."))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user