__HELPERS Cleaning and other things I decided to do. (#4584)

* Schizoposting

* The Crungly

* Tabbin' the JSON y'all

* Strings
This commit is contained in:
Zandario
2022-10-21 03:56:59 -05:00
committed by GitHub
parent 6397e624b9
commit ed05e01a95
207 changed files with 4315 additions and 3780 deletions

View File

@@ -1,4 +1,4 @@
## Citadel Station 13 RP
# Citadel Station 13 RP
[![Build Status](https://github.com/Citadel-Station-13/Citadel-Station-13-RP/workflows/CI%20Suite/badge.svg)](https://github.com/Citadel-Station-13/Citadel-Station-13-RP/actions?query=workflow%3A%22CI+Suite%22)
[![Percentage of issues still open](https://isitmaintained.com/badge/open/Citadel-Station-13/Citadel-Station-13-RP.svg)](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)

View File

@@ -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"

View File

@@ -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()

View File

@@ -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

View File

@@ -28,6 +28,9 @@
#define VAR_PROTECTED var
#endif
/proc/auxtools_stack_trace(msg)
CRASH(msg)
/proc/enable_debugging()
CRASH("Auxtools not found")

View 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__)

View File

@@ -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

View File

@@ -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())

View File

@@ -1,6 +1,3 @@
/proc/auxtools_stack_trace(msg)
CRASH(msg)
GLOBAL_LIST_EMPTY(auxtools_initialized)
#define AUXTOOLS_CHECK(LIB)\

View File

@@ -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))

View File

@@ -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)

View File

@@ -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)])"

View File

@@ -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.

View File

@@ -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

View File

@@ -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
View 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))

View File

@@ -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

View File

@@ -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)

View File

@@ -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]")

View File

@@ -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

View File

@@ -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
View 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]}"

View File

@@ -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)

View File

@@ -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

View File

@@ -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")
/*

View File

@@ -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
*/

View 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
*/

View File

@@ -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]")

View File

@@ -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]

View File

@@ -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))

View File

@@ -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()

View File

@@ -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))

View File

@@ -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)

View File

@@ -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]

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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
View 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)

View File

@@ -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
View File

@@ -0,0 +1,6 @@
/**
* A do nothing proc.
* There's a good reason we have this. I think. I hope.
*/
/proc/pass(...)
return

View File

@@ -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

View File

@@ -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
View 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]"

View File

@@ -1,5 +1,3 @@
/datum/controller/subsystem/ticker/proc/standard_reboot()
if(ready_for_reboot)
if(mode.station_was_nuked)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]

View 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
View 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()

View File

@@ -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))

View File

@@ -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 &lt;)
/// 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"="whod",
"whats"="whats",
"whatd"="whatd",
"thats"="thats",
"thatll"="thatll",
"thatd"="thatd",
" nows "=" nows ",
"isnt"="isnt",
" arent "=" arent ",
"wasnt"="wasnt",
"werent"="werent",
"havent"="havent",
"hasnt"="hasnt",
"hadnt"="hadnt",
"doesnt"="doesnt",
"didnt"="didnt",
"couldnt"="couldnt",
"wouldnt"="wouldnt",
"mustnt"="mustnt",
"shouldnt"="shouldnt",
" 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" = "whod",
"whats" = "whats",
"whatd" = "whatd",
"thats" = "thats",
"thatll" = "thatll",
"thatd" = "thatd",
" nows " = " nows ",
"isnt" = "isnt",
" arent " = " arent ",
"wasnt" = "wasnt",
"werent" = "werent",
"havent" = "havent",
"hasnt" = "hasnt",
"hadnt" = "hadnt",
"doesnt" = "doesnt",
"didnt" = "didnt",
"couldnt" = "couldnt",
"wouldnt" = "wouldnt",
"mustnt" = "mustnt",
"shouldnt" = "shouldnt",
))
/**
@@ -408,7 +434,9 @@
else
return "[copytext_preserve_html(string, 1, 37)]..."
/// Alternative copytext() for encoded text, doesn't break html entities (&#34; and other)
/**
* Alternative copytext() for encoded text, doesn't break html entities (&#34; 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("&#34;" = "\"","&#39;" = "'")
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, "")

View File

@@ -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)
/**

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View 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)

View File

@@ -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)

View File

@@ -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)*/ )

View File

@@ -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)))

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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))

View File

@@ -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)

View 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"))

View File

@@ -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

View File

@@ -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()

View File

@@ -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")

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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)

View File

@@ -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")

View File

@@ -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)
++.

View File

@@ -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()

View File

@@ -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()

View File

@@ -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]

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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