initial commit - cross reference with 5th port - obviously has compile errors

This commit is contained in:
LetterJay
2016-07-03 02:17:19 -05:00
commit 35a1723e98
4355 changed files with 2221257 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
//print a warning message to world.log
#define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [src] usr: [usr].")
/proc/warning(msg)
world.log << "## WARNING: [msg]"
//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)
world.log << "## NOTICE: [msg]"
//print a testing-mode debug message to world.log
/proc/testing(msg)
#ifdef TESTING
world.log << "## TESTING: [msg]"
#endif
/proc/log_admin(text)
admin_log.Add(text)
if (config.log_admin)
diary << "\[[time_stamp()]]ADMIN: [text]"
/proc/log_adminsay(text)
if (config.log_adminchat)
log_admin("ASAY: [text]")
/proc/log_dsay(text)
if (config.log_adminchat)
log_admin("DSAY: [text]")
/proc/log_game(text)
if (config.log_game)
diary << "\[[time_stamp()]]GAME: [text]"
/proc/log_vote(text)
if (config.log_vote)
diary << "\[[time_stamp()]]VOTE: [text]"
/proc/log_access(text)
if (config.log_access)
diary << "\[[time_stamp()]]ACCESS: [text]"
/proc/log_say(text)
if (config.log_say)
diary << "\[[time_stamp()]]SAY: [text]"
/proc/log_prayer(text)
if (config.log_prayer)
diary << "\[[time_stamp()]]PRAY: [text]"
/proc/log_law(text)
if (config.log_law)
diary << "\[[time_stamp()]]LAW: [text]"
/proc/log_ooc(text)
if (config.log_ooc)
diary << "\[[time_stamp()]]OOC: [text]"
/proc/log_whisper(text)
if (config.log_whisper)
diary << "\[[time_stamp()]]WHISPER: [text]"
/proc/log_emote(text)
if (config.log_emote)
diary << "\[[time_stamp()]]EMOTE: [text]"
/proc/log_attack(text)
if (config.log_attack)
diaryofmeanpeople << "\[[time_stamp()]]ATTACK: [text]"
/proc/log_pda(text)
if (config.log_pda)
diary << "\[[time_stamp()]]PDA: [text]"
/proc/log_comment(text)
if (config.log_pda)
//reusing the PDA option because I really don't think news comments are worth a config option
diary << "\[[time_stamp()]]COMMENT: [text]"
+41
View File
@@ -0,0 +1,41 @@
#define pick_list(FILE, KEY) (pick(strings(FILE, KEY)))
#define pick_list_replacements(FILE, KEY) (strings_replacement(FILE, KEY))
#define json_load(FILE) (json_decode(file2text(FILE)))
var/global/list/string_cache
var/global/string_filename_current_key
/proc/strings_replacement(filename, key)
load_strings_file(filename)
if((filename in string_cache) && (key in string_cache[filename]))
var/response = pick(string_cache[filename][key])
var/regex/r = regex("@pick\\((\\D+?)\\)", "g")
response = r.Replace(response, /proc/strings_subkey_lookup)
return response
else
CRASH("strings list not found: strings/[filename], index=[key]")
/proc/strings(filename as text, key as text)
load_strings_file(filename)
if((filename in string_cache) && (key in string_cache[filename]))
return string_cache[filename][key]
else
CRASH("strings list not found: strings/[filename], index=[key]")
/proc/strings_subkey_lookup(match, group1)
return pick_list(string_filename_current_key, group1)
/proc/load_strings_file(filename)
string_filename_current_key = filename
if(filename in string_cache)
return //no work to do
if(!string_cache)
string_cache = new
if(fexists("strings/[filename]"))
string_cache[filename] = json_load("strings/[filename]")
else
CRASH("file not found: strings/[filename]")
+36
View File
@@ -0,0 +1,36 @@
#define YOUNG 4
/client/proc/join_date_check(y,m,d)
var/DBQuery/query = dbcon.NewQuery("SELECT DATEDIFF(Now(),'[y]-[m]-[d]')")
if(!query.Execute())
world.log << "SQL ERROR doing datediff. Error : \[[query.ErrorMsg()]\]\n"
return FALSE
if(query.NextRow())
var/diff = text2num(query.item[1])
if(diff < YOUNG)
var/msg = "(IP: [address], ID: [computer_id]) is a new BYOND account made on [y]-[m]-[d]."
if(diff < 0)
msg += " They are also apparently from the future."
message_admins("[key_name_admin(src)] [msg]")
return TRUE
#undef YOUNG
/client/proc/findJoinDate()
var/http[] = world.Export("http://byond.com/members/[src.ckey]?format=text")
if(!http)
world.log << "Failed to connect to byond age check for [src.ckey]"
return FALSE
var/F = file2text(http["CONTENT"])
if(F)
var/regex/R = regex("joined = \"(\\d{4})-(\\d{2})-(\\d{2})\"")
if(!R.Find(F))
CRASH("Age check regex failed")
var/y = R.group[1]
var/m = R.group[2]
var/d = R.group[3]
return join_date_check(y,m,d)
+44
View File
@@ -0,0 +1,44 @@
/proc/cmp_numeric_dsc(a,b)
return b - a
/proc/cmp_numeric_asc(a,b)
return a - b
/proc/cmp_text_asc(a,b)
return sorttext(b,a)
/proc/cmp_text_dsc(a,b)
return sorttext(a,b)
/proc/cmp_name_asc(atom/a, atom/b)
return sorttext(b.name, a.name)
/proc/cmp_name_dsc(atom/a, atom/b)
return sorttext(a.name, b.name)
var/cmp_field = "name"
/proc/cmp_records_asc(datum/data/record/a, datum/data/record/b)
return sorttext(b.fields[cmp_field], a.fields[cmp_field])
/proc/cmp_records_dsc(datum/data/record/a, datum/data/record/b)
return sorttext(a.fields[cmp_field], b.fields[cmp_field])
/proc/cmp_ckey_asc(client/a, client/b)
return sorttext(b.ckey, a.ckey)
/proc/cmp_ckey_dsc(client/a, client/b)
return sorttext(a.ckey, b.ckey)
/proc/cmp_subsystem_init(datum/subsystem/a, datum/subsystem/b)
return b.init_order - a.init_order
/proc/cmp_subsystem_display(datum/subsystem/a, datum/subsystem/b)
if(a.display_order == b.display_order)
return sorttext(b.name, a.name)
return a.display_order - b.display_order
/proc/cmp_subsystem_priority(datum/subsystem/a, datum/subsystem/b)
return a.priority - b.priority
/proc/cmp_clientcolour_priority(datum/client_colour/A, datum/client_colour/B)
return B.priority - A.priority
+78
View File
@@ -0,0 +1,78 @@
//checks if a file exists and contains text
//returns text as a string if these conditions are met
/proc/return_file_text(filename)
if(fexists(filename) == 0)
throw EXCEPTION("return_file_text(): File not found")
return
var/text = file2text(filename)
if(!text)
throw EXCEPTION("return_file_text(): File empty")
return
return text
//Sends resource files to client cache
/client/proc/getFiles()
for(var/file in args)
src << browse_rsc(file)
/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm"))
var/path = root
for(var/i=0, i<max_iterations, i++)
var/list/choices = flist(path)
if(path != root)
choices.Insert(1,"/")
var/choice = input(src,"Choose a file to access:","Download",null) as null|anything in choices
switch(choice)
if(null)
return
if("/")
path = root
continue
path += choice
if(copytext(path,-1,0) != "/") //didn't choose a directory, no need to iterate again
break
var/extension = copytext(path,-4,0)
if( !fexists(path) || !(extension in valid_extensions) )
src << "<font color='red'>Error: browse_files(): File not found/Invalid file([path]).</font>"
return
return path
#define FTPDELAY 200 //200 tick delay to discourage spam
/* 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 = fileaccess_timer - world.time
if(time_to_wait > 0)
src << "<font color='red'>Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds.</font>"
return 1
fileaccess_timer = world.time + FTPDELAY
return 0
#undef FTPDELAY
/proc/pathwalk(path)
var/list/jobs = list(path)
var/list/filenames = list()
while(jobs.len)
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(findtext(new_filename, "/", -1))
jobs += current_dir + new_filename
else
filenames += current_dir + new_filename
return filenames
/proc/pathflatten(path)
return replacetext(path, "/", "_")
+446
View File
@@ -0,0 +1,446 @@
//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 Z_TURFS(ZLEVEL) block(locate(1,1,ZLEVEL), locate(world.maxx, world.maxy, ZLEVEL))
/proc/get_area(atom/A)
if (!istype(A))
return
for(A, A && !isarea(A), A=A.loc); //semicolon is for the empty statement
return A
/proc/get_area_master(O)
var/area/A = get_area(O)
if(A && A.master)
A = A.master
return A
/proc/get_area_name(N) //get area by its name
for(var/area/A in world)
if(A.name == N)
return A
return 0
/proc/get_areas_in_range(dist=0, atom/center=usr)
if(!dist)
var/turf/T = get_turf(center)
return T ? list(T.loc) : list()
if(!center)
return list()
var/list/turfs = RANGE_TURFS(dist, center)
var/list/areas = list()
for(var/V in turfs)
var/turf/T = V
areas |= T.loc
return areas
// Like view but bypasses luminosity check
/proc/get_hear(range, atom/source)
var/lum = source.luminosity
source.luminosity = 6
var/list/heard = view(range, source)
source.luminosity = lum
return heard
/proc/alone_in_area(area/the_area, mob/must_be_alone, check_type = /mob/living/carbon)
var/area/our_area = get_area_master(the_area)
for(var/C in living_mob_list)
if(!istype(C, check_type))
continue
if(C == must_be_alone)
continue
if(our_area == get_area_master(C))
return 0
return 1
//We used to use linear regression to approximate the answer, but Mloc realized this was actually faster.
//And lo and behold, it is, and it's more accurate to boot.
/proc/cheap_hypotenuse(Ax,Ay,Bx,By)
return sqrt(abs(Ax - Bx)**2 + abs(Ay - By)**2) //A squared + B squared = C squared
/proc/circlerange(center=usr,radius=3)
var/turf/centerturf = get_turf(center)
var/list/turfs = new/list()
var/rsq = radius * (radius+0.5)
for(var/atom/T in range(radius, centerturf))
var/dx = T.x - centerturf.x
var/dy = T.y - centerturf.y
if(dx*dx + dy*dy <= rsq)
turfs += T
//turfs += centerturf
return turfs
/proc/circleview(center=usr,radius=3)
var/turf/centerturf = get_turf(center)
var/list/atoms = new/list()
var/rsq = radius * (radius+0.5)
for(var/atom/A in view(radius, centerturf))
var/dx = A.x - centerturf.x
var/dy = A.y - centerturf.y
if(dx*dx + dy*dy <= rsq)
atoms += A
//turfs += centerturf
return atoms
/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj)
var/dx = Loc1.x - Loc2.x
var/dy = Loc1.y - Loc2.y
var/dist = sqrt(dx**2 + dy**2)
return dist
/proc/circlerangeturfs(center=usr,radius=3)
var/turf/centerturf = get_turf(center)
var/list/turfs = new/list()
var/rsq = radius * (radius+0.5)
for(var/turf/T in range(radius, centerturf))
var/dx = T.x - centerturf.x
var/dy = T.y - centerturf.y
if(dx*dx + dy*dy <= rsq)
turfs += T
return turfs
/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circlerangeturfs()?
var/turf/centerturf = get_turf(center)
var/list/turfs = new/list()
var/rsq = radius * (radius+0.5)
for(var/turf/T in view(radius, centerturf))
var/dx = T.x - centerturf.x
var/dy = T.y - centerturf.y
if(dx*dx + dy*dy <= rsq)
turfs += T
return turfs
//This is the new version of recursive_mob_check, used for say().
//The other proc was left intact because morgue trays use it.
//Sped this up again for real this time
/proc/recursive_hear_check(O)
var/list/processing_list = list(O)
. = list()
while(processing_list.len)
var/atom/A = processing_list[1]
if(A.flags & HEAR)
. += A
processing_list.Cut(1, 2)
processing_list += A.contents
// Better recursive loop, technically sort of not actually recursive cause that shit is retarded, enjoy.
//No need for a recursive limit either
/proc/recursive_mob_check(atom/O,client_check=1,sight_check=1,include_radio=1)
var/list/processing_list = list(O)
var/list/processed_list = list()
var/list/found_mobs = list()
while(processing_list.len)
var/atom/A = processing_list[1]
var/passed = 0
if(ismob(A))
var/mob/A_tmp = A
passed=1
if(client_check && !A_tmp.client)
passed=0
if(sight_check && !isInSight(A_tmp, O))
passed=0
else if(include_radio && istype(A, /obj/item/device/radio))
passed=1
if(sight_check && !isInSight(A, O))
passed=0
if(passed)
found_mobs |= A
for(var/atom/B in A)
if(!processed_list[B])
processing_list |= B
processing_list.Cut(1, 2)
processed_list[A] = A
return found_mobs
/proc/get_hearers_in_view(R, atom/source)
// Returns a list of hearers in view(R) from source (ignoring luminosity). Used in saycode.
var/turf/T = get_turf(source)
var/list/hear = list()
if(!T)
return hear
var/list/range = get_hear(R, T)
for(var/atom/movable/A in range)
hear |= recursive_hear_check(A)
return hear
/proc/get_mobs_in_radio_ranges(list/obj/item/device/radio/radios)
set background = BACKGROUND_ENABLED
. = list()
// Returns a list of mobs who can hear any of the radios given in @radios
for(var/obj/item/device/radio/R in radios)
if(R)
. |= get_hearers_in_view(R.canhear_range, R)
#define SIGN(X) ((X<0)?-1:1)
/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5)
var/turf/T
if(X1==X2)
if(Y1==Y2)
return 1 //Light cannot be blocked on same tile
else
var/s = SIGN(Y2-Y1)
Y1+=s
while(Y1!=Y2)
T=locate(X1,Y1,Z)
if(T.opacity)
return 0
Y1+=s
else
var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1))
var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles
var/signX = SIGN(X2-X1)
var/signY = SIGN(Y2-Y1)
if(X1<X2)
b+=m
while(X1!=X2 || Y1!=Y2)
if(round(m*X1+b-Y1))
Y1+=signY //Line exits tile vertically
else
X1+=signX //Line exits tile horizontally
T=locate(X1,Y1,Z)
if(T.opacity)
return 0
return 1
#undef SIGN
/proc/isInSight(atom/A, atom/B)
var/turf/Aturf = get_turf(A)
var/turf/Bturf = get_turf(B)
if(!Aturf || !Bturf)
return 0
if(inLineOfSight(Aturf.x,Aturf.y, Bturf.x,Bturf.y,Aturf.z))
return 1
else
return 0
/proc/get_cardinal_step_away(atom/start, atom/finish) //returns the position of a step from start away from finish, in one of the cardinal directions
//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)
if(dy > 0)
return get_step(start, SOUTH)
else
return get_step(start, NORTH)
else
if(dx > 0)
return get_step(start, WEST)
else
return get_step(start, EAST)
/proc/try_move_adjacent(atom/movable/AM)
var/turf/T = get_turf(AM)
for(var/direction in cardinal)
if(AM.Move(get_step(T, direction)))
break
/proc/get_mob_by_key(key)
for(var/mob/M in mob_list)
if(M.ckey == lowertext(key))
return M
return null
// 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_candidates(be_special_type, afk_bracket=3000, var/jobbanType)
var/list/candidates = list()
// Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000))
while(!candidates.len && afk_bracket < 6000)
for(var/mob/dead/observer/G in player_list)
if(G.client != null)
if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD))
if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special))
if (jobbanType)
if(!(jobban_isbanned(G, jobbanType) || jobban_isbanned(G, "Syndicate")))
candidates += G.client
else
candidates += G.client
afk_bracket += 600 // Add a minute to the bracket, for every attempt
return candidates
/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480)
if(!isobj(O))
O = new /obj/screen/text()
O.maptext = maptext
O.maptext_height = maptext_height
O.maptext_width = maptext_width
O.screen_loc = screen_loc
return O
/proc/Show2Group4Delay(obj/O, list/group, delay=0)
if(!isobj(O))
return
if(!group)
group = clients
for(var/client/C in group)
C.screen += O
if(delay)
spawn(delay)
for(var/client/C in group)
C.screen -= O
/proc/flick_overlay(image/I, list/show_to, duration)
for(var/client/C in show_to)
C.images += I
spawn(duration)
for(var/client/C in show_to)
C.images -= I
/proc/get_active_player_count(var/alive_check = 0, var/afk_check = 0, var/human_check = 0)
// Get active players who are playing in the round
var/active_players = 0
for(var/i = 1; i <= player_list.len; i++)
var/mob/M = player_list[i]
if(M && M.client)
if(alive_check && M.stat)
continue
else if(afk_check && M.client.is_afk())
continue
else if(human_check && !istype(M, /mob/living/carbon/human))
continue
else if(istype(M, /mob/new_player)) // exclude people in the lobby
continue
else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers)
var/mob/dead/observer/O = M
if(O.started_as_observer) // Exclude people who started as observers
continue
active_players++
return active_players
/datum/projectile_data
var/src_x
var/src_y
var/time
var/distance
var/power_x
var/power_y
var/dest_x
var/dest_y
/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \
var/power_x, var/power_y, var/dest_x, var/dest_y)
src.src_x = src_x
src.src_y = src_y
src.time = time
src.distance = distance
src.power_x = power_x
src.power_y = power_y
src.dest_x = dest_x
src.dest_y = dest_y
/proc/projectile_trajectory(src_x, src_y, rotation, angle, power)
// returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle],
// rotated at [rotation] and with the power of [power]
// Thanks to VistaPOWA for this function
var/power_x = power * cos(angle)
var/power_y = power * sin(angle)
var/time = 2* power_y / 10 //10 = g
var/distance = time * power_x
var/dest_x = src_x + distance*sin(rotation);
var/dest_y = src_y + distance*cos(rotation);
return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y)
/proc/pollCandidates(var/Question, var/jobbanType, var/datum/game_mode/gametypeCheck, var/be_special_flag = 0, var/poll_time = 300)
var/list/mob/dead/observer/candidates = list()
var/time_passed = world.time
if (!Question)
Question = "Would you like to be a special role?"
for(var/mob/dead/observer/G in player_list)
if(!G.key || !G.client)
continue
if(be_special_flag)
if(!(G.client.prefs) || !(be_special_flag in G.client.prefs.be_special))
continue
if (gametypeCheck)
if(!gametypeCheck.age_check(G.client))
continue
if (jobbanType)
if(jobban_isbanned(G, jobbanType) || jobban_isbanned(G, "Syndicate"))
continue
spawn(0)
G << 'sound/misc/notice2.ogg' //Alerting them to their consideration
switch(askuser(G,Question,"Please answer in [poll_time/10] seconds!","Yes","No", StealFocus=0, Timeout=poll_time))
if(1)
G << "<span class='notice'>Choice registered: Yes.</span>"
if((world.time-time_passed)>poll_time)
G << "<span class='danger'>Sorry, you were too late for the consideration!</span>"
G << 'sound/machines/buzz-sigh.ogg'
else
candidates += G
if(2)
G << "<span class='danger'>Choice registered: No.</span>"
sleep(poll_time)
//Check all our candidates, to make sure they didn't log off during the wait period.
for(var/mob/dead/observer/G in candidates)
if(!G.key || !G.client)
candidates.Remove(G)
return candidates
/proc/makeBody(mob/dead/observer/G_found) // Uses stripped down and bastardized code from respawn character
if(!G_found || !G_found.key)
return
//First we spawn a dude.
var/mob/living/carbon/human/new_character = new(pick(latejoin))//The mob being spawned.
G_found.client.prefs.copy_to(new_character)
new_character.dna.update_dna_identity()
new_character.key = G_found.key
return new_character
+84
View File
@@ -0,0 +1,84 @@
//////////////////////////
/////Initial Building/////
//////////////////////////
/proc/make_datum_references_lists()
//hair
init_sprite_accessory_subtypes(/datum/sprite_accessory/hair, hair_styles_list, hair_styles_male_list, hair_styles_female_list)
//facial hair
init_sprite_accessory_subtypes(/datum/sprite_accessory/facial_hair, facial_hair_styles_list, facial_hair_styles_male_list, facial_hair_styles_female_list)
//underwear
init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, underwear_list, underwear_m, underwear_f)
//undershirt
init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, undershirt_list, undershirt_m, undershirt_f)
//socks
init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, socks_list)
//lizard bodyparts (blizzard intensifies)
init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, body_markings_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/lizard, tails_list_lizard)
init_sprite_accessory_subtypes(/datum/sprite_accessory/tails_animated/lizard, animated_tails_list_lizard)
init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/human, tails_list_human)
init_sprite_accessory_subtypes(/datum/sprite_accessory/tails_animated/human, animated_tails_list_human)
init_sprite_accessory_subtypes(/datum/sprite_accessory/snouts, snouts_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/horns, horns_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/ears, ears_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/wings, wings_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/wings_open, wings_open_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/frills, frills_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/spines, spines_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/spines_animated, animated_spines_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/wings, r_wings_list,roundstart = TRUE)
//Species
for(var/spath in subtypesof(/datum/species))
var/datum/species/S = new spath()
if(S.roundstart)
roundstart_species[S.id] = S.type
species_list[S.id] = S.type
//Surgeries
for(var/path in subtypesof(/datum/surgery))
surgeries_list += new path()
//Materials
for(var/path in subtypesof(/datum/material))
var/datum/material/D = new path()
materials_list[D.id] = D
//Techs
for(var/path in subtypesof(/datum/tech))
var/datum/tech/D = new path()
tech_list[D.id] = D
init_subtypes(/datum/crafting_recipe, crafting_recipes)
/* // Uncomment to debug chemical reaction list.
/client/verb/debug_chemical_list()
for (var/reaction in chemical_reactions_list)
. += "chemical_reactions_list\[\"[reaction]\"\] = \"[chemical_reactions_list[reaction]]\"\n"
if(islist(chemical_reactions_list[reaction]))
var/list/L = chemical_reactions_list[reaction]
for(var/t in L)
. += " has: [t]\n"
world << .
*/
//creates every subtype of prototype (excluding prototype) and adds it to list L.
//if no list/L is provided, one is created.
/proc/init_subtypes(prototype, list/L)
if(!istype(L))
L = list()
for(var/path in subtypesof(prototype))
L += new path()
return L
//returns a list of paths to every subtype of prototype (excluding prototype)
//if no list/L is provided, one is created.
/proc/init_paths(prototype, list/L)
if(!istype(L))
L = list()
for(var/path in subtypesof(prototype))
L+= path
return L
+389
View File
@@ -0,0 +1,389 @@
//generic (by snowflake) tile smoothing code; smooth your icons with this!
/*
Each tile is divided in 4 corners, each corner has an image associated to it; the tile is then overlayed by these 4 images
To use this, just set your atom's 'smooth' 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 types you want the atom icon to smooth with in 'canSmoothWith' INCLUDING THE TYPE OF THE ATOM ITSELF.
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' flag to the atom's smooth 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/shuttle' and its subtypes.
*/
//Redefinitions of the diagonal directions so they can be stored in one var without conflicts
#define N_NORTH 2
#define N_SOUTH 4
#define N_EAST 16
#define N_WEST 256
#define N_NORTHEAST 32
#define N_NORTHWEST 512
#define N_SOUTHEAST 64
#define N_SOUTHWEST 1024
#define SMOOTH_FALSE 0 //not smooth
#define SMOOTH_TRUE 1 //smooths with exact specified types or just itself
#define SMOOTH_MORE 2 //smooths with all subtypes of specified types or just itself (this value can replace SMOOTH_TRUE)
#define SMOOTH_DIAGONAL 4 //if atom should smooth diagonally, this should be present in 'smooth' var
#define SMOOTH_BORDER 8 //atom will smooth with the borders of the map
#define NULLTURF_BORDER 123456789
#define DEFAULT_UNDERLAY_ICON 'icons/turf/floors.dmi'
#define DEFAULT_UNDERLAY_ICON_STATE "plating"
#define DEFAULT_UNDERLAY_IMAGE image(DEFAULT_UNDERLAY_ICON, DEFAULT_UNDERLAY_ICON_STATE)
/atom/var/smooth = SMOOTH_FALSE
/atom/var/top_left_corner
/atom/var/top_right_corner
/atom/var/bottom_left_corner
/atom/var/bottom_right_corner
/atom/var/list/canSmoothWith = null // TYPE PATHS I CAN SMOOTH WITH~~~~~ If this is null and atom is smooth, it smooths only with itself
/atom/movable/var/can_be_unanchored = 0
/turf/var/list/fixed_underlay = null
/proc/calculate_adjacencies(atom/A)
if(!A.loc)
return 0
var/adjacencies = 0
var/atom/movable/AM
if(istype(A, /atom/movable))
AM = A
if(AM.can_be_unanchored && !AM.anchored)
return 0
for(var/direction in cardinal)
AM = find_type_in_direction(A, direction)
if(AM == NULLTURF_BORDER)
if((A.smooth & SMOOTH_BORDER))
adjacencies |= 1 << direction
else if( (AM && !istype(AM)) || (istype(AM) && AM.anchored) )
adjacencies |= 1 << direction
if(adjacencies & N_NORTH)
if(adjacencies & N_WEST)
AM = find_type_in_direction(A, NORTHWEST)
if(AM == NULLTURF_BORDER)
if((A.smooth & SMOOTH_BORDER))
adjacencies |= N_NORTHWEST
else if( (AM && !istype(AM)) || (istype(AM) && AM.anchored) )
adjacencies |= N_NORTHWEST
if(adjacencies & N_EAST)
AM = find_type_in_direction(A, NORTHEAST)
if(AM == NULLTURF_BORDER)
if((A.smooth & SMOOTH_BORDER))
adjacencies |= N_NORTHEAST
else if( (AM && !istype(AM)) || (istype(AM) && AM.anchored) )
adjacencies |= N_NORTHEAST
if(adjacencies & N_SOUTH)
if(adjacencies & N_WEST)
AM = find_type_in_direction(A, SOUTHWEST)
if(AM == NULLTURF_BORDER)
if((A.smooth & SMOOTH_BORDER))
adjacencies |= N_SOUTHWEST
else if( (AM && !istype(AM)) || (istype(AM) && AM.anchored) )
adjacencies |= N_SOUTHWEST
if(adjacencies & N_EAST)
AM = find_type_in_direction(A, SOUTHEAST)
if(AM == NULLTURF_BORDER)
if((A.smooth & SMOOTH_BORDER))
adjacencies |= N_SOUTHEAST
else if( (AM && !istype(AM)) || (istype(AM) && AM.anchored) )
adjacencies |= N_SOUTHEAST
return adjacencies
/proc/smooth_icon(atom/A)
if(qdeleted(A))
return
if(!A || !A.smooth)
return
spawn(0)
if((A.smooth & SMOOTH_TRUE) || (A.smooth & SMOOTH_MORE))
var/adjacencies = calculate_adjacencies(A)
if(A.smooth & SMOOTH_DIAGONAL)
A.diagonal_smooth(adjacencies)
else
cardinal_smooth(A, adjacencies)
/atom/proc/diagonal_smooth(adjacencies)
switch(adjacencies)
if(N_NORTH|N_WEST)
replace_smooth_overlays("d1-se-0","d2-se","d3-se","d4-se")
if(N_NORTH|N_EAST)
replace_smooth_overlays("d1-sw","d2-sw-0","d3-sw","d4-sw")
if(N_SOUTH|N_WEST)
replace_smooth_overlays("d1-ne","d2-ne","d3-ne-0","d4-ne")
if(N_SOUTH|N_EAST)
replace_smooth_overlays("d1-nw","d2-nw","d3-nw","d4-nw-0")
if(N_NORTH|N_WEST|N_NORTHWEST)
replace_smooth_overlays("d1-se-1","d2-se","d3-se","d4-se")
if(N_NORTH|N_EAST|N_NORTHEAST)
replace_smooth_overlays("d1-sw","d2-sw-1","d3-sw","d4-sw")
if(N_SOUTH|N_WEST|N_SOUTHWEST)
replace_smooth_overlays("d1-ne","d2-ne","d3-ne-1","d4-ne")
if(N_SOUTH|N_EAST|N_SOUTHEAST)
replace_smooth_overlays("d1-nw","d2-nw","d3-nw","d4-nw-1")
else
cardinal_smooth(src, adjacencies)
return
icon_state = ""
return adjacencies
//only walls should have a need to handle underlays
/turf/closed/wall/diagonal_smooth(adjacencies)
adjacencies = reverse_ndir(..())
if(adjacencies)
underlays.Cut()
if(fixed_underlay)
if(fixed_underlay["space"])
underlays += image('icons/turf/space.dmi', SPACE_ICON_STATE, layer=src.layer)
else
underlays += image(fixed_underlay["icon"], fixed_underlay["icon_state"], layer=src.layer)
else
var/turf/T = get_step(src, turn(adjacencies, 180))
if(T && T.density)
T = get_step(src, turn(adjacencies, 135))
if(T && T.density)
T = get_step(src, turn(adjacencies, 225))
if(istype(T, /turf/open/space))
underlays += image('icons/turf/space.dmi', SPACE_ICON_STATE, layer=src.layer)
else if(T && !T.density && !T.smooth)
underlays += T
else
underlays += DEFAULT_UNDERLAY_IMAGE
/proc/cardinal_smooth(atom/A, adjacencies)
//NW CORNER
var/nw = "1-i"
if((adjacencies & N_NORTH) && (adjacencies & N_WEST))
if(adjacencies & N_NORTHWEST)
nw = "1-f"
else
nw = "1-nw"
else
if(adjacencies & N_NORTH)
nw = "1-n"
else if(adjacencies & N_WEST)
nw = "1-w"
//NE CORNER
var/ne = "2-i"
if((adjacencies & N_NORTH) && (adjacencies & N_EAST))
if(adjacencies & N_NORTHEAST)
ne = "2-f"
else
ne = "2-ne"
else
if(adjacencies & N_NORTH)
ne = "2-n"
else if(adjacencies & N_EAST)
ne = "2-e"
//SW CORNER
var/sw = "3-i"
if((adjacencies & N_SOUTH) && (adjacencies & N_WEST))
if(adjacencies & N_SOUTHWEST)
sw = "3-f"
else
sw = "3-sw"
else
if(adjacencies & N_SOUTH)
sw = "3-s"
else if(adjacencies & N_WEST)
sw = "3-w"
//SE CORNER
var/se = "4-i"
if((adjacencies & N_SOUTH) && (adjacencies & N_EAST))
if(adjacencies & N_SOUTHEAST)
se = "4-f"
else
se = "4-se"
else
if(adjacencies & N_SOUTH)
se = "4-s"
else if(adjacencies & N_EAST)
se = "4-e"
if(A.top_left_corner != nw)
A.overlays -= A.top_left_corner
A.top_left_corner = nw
A.add_overlay(nw)
if(A.top_right_corner != ne)
A.overlays -= A.top_right_corner
A.top_right_corner = ne
A.add_overlay(ne)
if(A.bottom_right_corner != sw)
A.overlays -= A.bottom_right_corner
A.bottom_right_corner = sw
A.add_overlay(sw)
if(A.bottom_left_corner != se)
A.overlays -= A.bottom_left_corner
A.bottom_left_corner = se
A.add_overlay(se)
/proc/find_type_in_direction(atom/source, direction)
var/turf/target_turf = get_step(source, direction)
if(!target_turf)
return NULLTURF_BORDER
if(source.canSmoothWith)
var/atom/A
if(source.smooth & SMOOTH_MORE)
for(var/a_type in source.canSmoothWith)
if( istype(target_turf, a_type) )
return target_turf
A = locate(a_type) in target_turf
if(A)
return A
return null
for(var/a_type in source.canSmoothWith)
if(a_type == target_turf.type)
return target_turf
A = locate(a_type) in target_turf
if(A && A.type == a_type)
return A
return null
else
if(isturf(source))
return source.type == target_turf.type ? target_turf : null
var/atom/A = locate(source.type) in target_turf
return A && A.type == source.type ? A : null
//Icon smoothing helpers
/proc/smooth_zlevel(var/zlevel, now = FALSE)
var/list/away_turfs = block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel))
for(var/V in away_turfs)
var/turf/T = V
if(T.smooth)
if(now)
smooth_icon(T)
else
queue_smooth(T)
for(var/R in T)
var/atom/A = R
if(A.smooth)
if(now)
smooth_icon(A)
else
queue_smooth(A)
/atom/proc/clear_smooth_overlays()
overlays -= top_left_corner
top_left_corner = null
overlays -= top_right_corner
top_right_corner = null
overlays -= bottom_right_corner
bottom_right_corner = null
overlays -= bottom_left_corner
bottom_left_corner = null
/atom/proc/replace_smooth_overlays(nw, ne, sw, se)
clear_smooth_overlays()
top_left_corner = nw
add_overlay(nw)
top_right_corner = ne
add_overlay(ne)
bottom_left_corner = sw
add_overlay(sw)
bottom_right_corner = se
add_overlay(se)
/proc/reverse_ndir(ndir)
switch(ndir)
if(N_NORTH)
return NORTH
if(N_SOUTH)
return SOUTH
if(N_WEST)
return WEST
if(N_EAST)
return EAST
if(N_NORTHWEST)
return NORTHWEST
if(N_NORTHEAST)
return NORTHEAST
if(N_SOUTHEAST)
return SOUTHEAST
if(N_SOUTHWEST)
return SOUTHWEST
if(N_NORTH|N_WEST)
return NORTHWEST
if(N_NORTH|N_EAST)
return NORTHEAST
if(N_SOUTH|N_WEST)
return SOUTHWEST
if(N_SOUTH|N_EAST)
return SOUTHEAST
if(N_NORTH|N_WEST|N_NORTHWEST)
return NORTHWEST
if(N_NORTH|N_EAST|N_NORTHEAST)
return NORTHEAST
if(N_SOUTH|N_WEST|N_SOUTHWEST)
return SOUTHWEST
if(N_SOUTH|N_EAST|N_SOUTHEAST)
return SOUTHEAST
else
return 0
//SSicon_smooth
/proc/ss_smooth_icon(atom/A)
if(qdeleted(A))
return
if(!istype(A) || (A && !A.smooth))
return
if((A.smooth & SMOOTH_TRUE) || (A.smooth & SMOOTH_MORE))
var/adjacencies = calculate_adjacencies(A)
if(A.smooth & SMOOTH_DIAGONAL)
A.diagonal_smooth(adjacencies)
else
cardinal_smooth(A, adjacencies)
//SSicon_smooth
/proc/queue_smooth_neighbors(atom/A)
for(var/V in orange(1,A))
var/atom/T = V
if(T.smooth)
queue_smooth(T)
//SSicon_smooth
/proc/queue_smooth(atom/A)
if(SSicon_smooth)
SSicon_smooth.smooth_queue[A] = A
SSicon_smooth.can_fire = 1
else
smooth_icon(A)
//Example smooth wall
/turf/closed/wall/smooth
name = "smooth wall"
icon = 'icons/turf/smooth_wall.dmi'
icon_state = "smooth"
walltype = "shuttle"
smooth = SMOOTH_TRUE|SMOOTH_DIAGONAL|SMOOTH_BORDER
canSmoothWith = null
File diff suppressed because it is too large Load Diff
+388
View File
@@ -0,0 +1,388 @@
/*
* Holds procs to help with list operations
* Contains groups:
* Misc
* Sorting
*/
/*
* Misc
*/
//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)
return "[nothing_text]"
else if (total == 1)
return "[input[1]]"
else if (total == 2)
return "[input[1]][and_text][input[2]]"
else
var/output = ""
var/index = 1
while (index < total)
if (index == total - 1)
comma_text = final_comma_text
output += "[input[index]][comma_text]"
index++
return "[output][and_text][input[index]]"
//Returns list element or null. Should prevent "index out of bounds" error.
/proc/listgetindex(list/L, index)
if(istype(L))
if(isnum(index) && IsInteger(index))
if(IsInRange(index,1,L.len))
return L[index]
else if(index in L)
return L[index]
return
//Return either pick(list) or null if list is not of type /list or is empty
/proc/safepick(list/L)
if(istype(L) && L.len)
return pick(L)
//Checks if the list is empty
/proc/isemptylist(list/L)
if(!L.len)
return 1
return 0
//Checks for specific types in a list
/proc/is_type_in_list(atom/A, list/L)
if(!L || !L.len || !A)
return 0
for(var/type in L)
if(istype(A, type))
return 1
return 0
//Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches')
/proc/is_type_in_typecache(atom/A, list/L)
if(!L || !L.len || !A)
return 0
return L[A.type]
//Like typesof() or subtypesof(), but returns a typecache instead of a list
/proc/typecacheof(path, ignore_root_path)
if(ispath(path))
var/list/types = ignore_root_path ? subtypesof(path) : typesof(path)
var/list/L = list()
for(var/T in types)
L[T] = TRUE
return L
else if(islist(path))
var/list/pathlist = path
var/list/L = list()
if(ignore_root_path)
for(var/P in pathlist)
for(var/T in subtypesof(P))
L[T] = TRUE
else
for(var/P in pathlist)
for(var/T in typesof(P))
L[T] = TRUE
return L
//Empties the list by setting the length to 0. Hopefully the elements get garbage collected
/proc/clearlist(list/list)
if(istype(list))
list.len = 0
return
//Removes any null entries from the list
/proc/listclearnulls(list/L)
if(istype(L))
var/i=1
for(var/thing in L)
if(thing != null)
++i
continue
L.Cut(i,i+1)
/*
* Returns list containing all the entries from first list that are not present in second.
* If skiprep = 1, repeated elements are treated as one.
* If either of arguments is not a list, returns null
*/
/proc/difflist(list/first, list/second, skiprep=0)
if(!islist(first) || !islist(second))
return
var/list/result = new
if(skiprep)
for(var/e in first)
if(!(e in result) && !(e in second))
result += e
else
result = first - second
return result
/*
* Returns list containing entries that are in either list but not both.
* If skipref = 1, repeated elements are treated as one.
* If either of arguments is not a list, returns null
*/
/proc/uniquemergelist(list/first, list/second, skiprep=0)
if(!islist(first) || !islist(second))
return
var/list/result = new
if(skiprep)
result = difflist(first, second, skiprep)+difflist(second, first, skiprep)
else
result = first ^ second
return result
//Pretends to pick an element based on its weight but really just seems to pick a random element.
/proc/pickweight(list/L)
var/total = 0
var/item
for (item in L)
if (!L[item])
L[item] = 1
total += L[item]
total = rand(1, total)
for (item in L)
total -=L [item]
if (total <= 0)
return item
return null
//Pick a random element from the list and remove it from the list.
/proc/pick_n_take(list/L)
if(L.len)
var/picked = rand(1,L.len)
. = L[picked]
L.Cut(picked,picked+1) //Cut is far more efficient that Remove()
//Returns the top(last) element from the list and removes it from the list (typical stack function)
/proc/pop(list/L)
if(L.len)
. = L[L.len]
L.len--
/proc/popleft(list/L)
if(L.len)
. = L[1]
L.Cut(1,2)
/proc/sorted_insert(list/L, thing, comparator)
var/pos = L.len
while(pos > 0 && call(comparator)(thing, L[pos]) > 0)
pos--
L.Insert(pos+1, thing)
// Returns the next item in a list
/proc/next_list_item(item, list/L)
var/i
i = L.Find(item)
if(i == L.len)
i = 1
else
i++
return L[i]
// Returns the previous item in a list
/proc/previous_list_item(item, list/L)
var/i
i = L.Find(item)
if(i == 1)
i = L.len
else
i--
return L[i]
/*
* Sorting
*/
/*
//Reverses the order of items in the list
/proc/reverselist(list/input)
var/list/output = list()
for(var/i = input.len; i >= 1; i--)
output += input[i]
return output
*/
//Randomize: Return the list in a random order
/proc/shuffle(list/L)
if(!L)
return
L = L.Copy()
for(var/i=1, i<L.len, ++i)
L.Swap(i,rand(i,L.len))
return L
//Return a list with no duplicate entries
/proc/uniqueList(list/L)
. = list()
for(var/i in L)
. |= i
//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)
//Specifically for record datums in a list.
/proc/sortRecord(list/L, field = "name", order = 1)
cmp_field = field
return sortTim(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)
//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)
//Converts a bitfield to a list of numbers (or words if a wordlist is provided)
/proc/bitfield2list(bitfield = 0, list/wordlist)
var/list/r = list()
if(istype(wordlist,/list))
var/max = min(wordlist.len,16)
var/bit = 1
for(var/i=1, i<=max, i++)
if(bitfield & bit)
r += wordlist[i]
bit = bit << 1
else
for(var/bit=1, bit<=65535, bit = bit << 1)
if(bitfield & bit)
r += bit
return r
// Returns the key based on the index
#define KEYBYINDEX(L, index) (((index <= L:len) && (index > 0)) ? L[index] : null)
/proc/count_by_type(list/L, type)
var/i = 0
for(var/T in L)
if(istype(T, type))
i++
return i
/proc/find_record(field, value, list/L)
for(var/datum/data/record/R in L)
if(R.fields[field] == value)
return R
//Move a single element from position fromIndex within a list, to position toIndex
//All elements in the range [1,toIndex) before the move will be before the pivot afterwards
//All elements in the range [toIndex, L.len+1) before the move will be after the pivot afterwards
//In other words, it's as if the range [fromIndex,toIndex) have been rotated using a <<< operation common to other languages.
//fromIndex and toIndex must be in the range [1,L.len+1]
//This will preserve associations ~Carnie
/proc/moveElement(list/L, fromIndex, toIndex)
if(fromIndex == toIndex || fromIndex+1 == toIndex) //no need to move
return
if(fromIndex > toIndex)
++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one
L.Insert(toIndex, null)
L.Swap(fromIndex, toIndex)
L.Cut(fromIndex, fromIndex+1)
//Move elements [fromIndex,fromIndex+len) to [toIndex-len, toIndex)
//Same as moveElement but for ranges of elements
//This will preserve associations ~Carnie
/proc/moveRange(list/L, fromIndex, toIndex, len=1)
var/distance = abs(toIndex - fromIndex)
if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements
if(fromIndex <= toIndex)
return //no need to move
fromIndex += len //we want to shift left instead of right
for(var/i=0, i<distance, ++i)
L.Insert(fromIndex, null)
L.Swap(fromIndex, toIndex)
L.Cut(toIndex, toIndex+1)
else
if(fromIndex > toIndex)
fromIndex += len
for(var/i=0, i<len, ++i)
L.Insert(toIndex, null)
L.Swap(fromIndex, toIndex)
L.Cut(fromIndex, fromIndex+1)
//Move elements from [fromIndex, fromIndex+len) to [toIndex, toIndex+len)
//Move any elements being overwritten by the move to the now-empty elements, preserving order
//Note: if the two ranges overlap, only the destination order will be preserved fully, since some elements will be within both ranges ~Carnie
/proc/swapRange(list/L, fromIndex, toIndex, len=1)
var/distance = abs(toIndex - fromIndex)
if(len > distance) //there is an overlap, therefore swapping each element will require more swaps than inserting new elements
if(fromIndex < toIndex)
toIndex += len
else
fromIndex += len
for(var/i=0, i<distance, ++i)
L.Insert(fromIndex, null)
L.Swap(fromIndex, toIndex)
L.Cut(toIndex, toIndex+1)
else
if(toIndex > fromIndex)
var/a = toIndex
toIndex = fromIndex
fromIndex = a
for(var/i=0, i<len, ++i)
L.Swap(fromIndex++, toIndex++)
//replaces reverseList ~Carnie
/proc/reverseRange(list/L, start=1, end=0)
if(L.len)
start = start % L.len
end = end % (L.len+1)
if(start <= 0)
start += L.len
if(end <= 0)
end += L.len + 1
--end
while(start < end)
L.Swap(start++,end--)
return L
//return first thing in L which has var/varname == value
//this is typecaste as list/L, but you could actually feed it an atom instead.
//completely safe to use
/proc/getElementByVar(list/L, varname, value)
varname = "[varname]"
for(var/datum/D in L)
if(D.vars.Find(varname))
if(D.vars[varname] == value)
return D
//remove all nulls from a list
/proc/removeNullsFromList(list/L)
while(L.Remove(null))
continue
return L
//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
. = l.Copy()
for(var/i = 1 to l.len)
if(islist(.[i]))
.[i] = .(.[i])
//Picks from the list, with some safeties, and returns the "default" arg if it fails
#define DEFAULTPICK(L, default) ((istype(L, /list) && L:len) ? pick(L) : default)
+167
View File
@@ -0,0 +1,167 @@
// Credits to Nickr5 for the useful procs I've taken from his library resource.
var/const/E = 2.71828183
var/const/Sqrt2 = 1.41421356
// List of square roots for the numbers 1-100.
var/list/sqrtTable = list(1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10)
/proc/sign(x)
return x!=0?x/abs(x):0
/proc/Atan2(x, y)
if(!x && !y) return 0
var/a = arccos(x / sqrt(x*x + y*y))
return y >= 0 ? a : -a
/proc/Ceiling(x, y=1)
return -round(-x / y) * y
#define Clamp(CLVALUE,CLMIN,CLMAX) ( max( (CLMIN), min((CLVALUE), (CLMAX)) ) )
// cotangent
/proc/Cot(x)
return 1 / Tan(x)
// cosecant
/proc/Csc(x)
return 1 / sin(x)
/proc/Default(a, b)
return a ? a : b
// Greatest Common Divisor - Euclid's algorithm
/proc/Gcd(a, b)
return b ? Gcd(b, a % b) : a
/proc/Inverse(x)
return 1 / x
/proc/IsAboutEqual(a, b, deviation = 0.1)
return abs(a - b) <= deviation
/proc/IsEven(x)
return x % 2 == 0
// Returns true if val is from min to max, inclusive.
/proc/IsInRange(val, min, max)
return min <= val && val <= max
/proc/IsInteger(x)
return round(x) == x
/proc/IsOdd(x)
return !IsEven(x)
/proc/IsMultiple(x, y)
return x % y == 0
// Least Common Multiple
/proc/Lcm(a, b)
return abs(a) / Gcd(a, b) * abs(b)
// Performs a linear interpolation between a and b.
// Note that amount=0 returns a, amount=1 returns b, and
// amount=0.5 returns the mean of a and b.
/proc/Lerp(a, b, amount = 0.5)
return a + (b - a) * amount
//Calculates the sum of a list of numbers.
/proc/Sum(var/list/data)
. = 0
for(var/val in data)
.+= val
//Calculates the mean of a list of numbers.
/proc/Mean(var/list/data)
. = Sum(data) / (data.len)
// Returns the nth root of x.
/proc/Root(n, x)
return x ** (1 / n)
// secant
/proc/Sec(x)
return 1 / cos(x)
// The quadratic formula. Returns a list with the solutions, or an empty list
// if they are imaginary.
/proc/SolveQuadratic(a, b, c)
ASSERT(a)
. = list()
var/d = b*b - 4 * a * c
var/bottom = 2 * a
if(d < 0) return
var/root = sqrt(d)
. += (-b + root) / bottom
if(!d) return
. += (-b - root) / bottom
// tangent
/proc/Tan(x)
return sin(x) / cos(x)
/proc/ToDegrees(radians)
// 180 / Pi
return radians * 57.2957795
/proc/ToRadians(degrees)
// Pi / 180
return degrees * 0.0174532925
// Will filter out extra rotations and negative rotations
// E.g: 540 becomes 180. -180 becomes 180.
/proc/SimplifyDegrees(degrees)
degrees = degrees % 360
if(degrees < 0)
degrees += 360
return degrees
// min is inclusive, max is exclusive
/proc/Wrap(val, min, max)
var/d = max - min
var/t = round((val - min) / d)
return val - (t * d)
//A logarithm that converts an integer to a number scaled between 0 and 1 (can be tweaked to be higher).
//Currently, this is used for hydroponics-produce sprite transforming, but could be useful for other transform functions.
/proc/TransformUsingVariable(input, inputmaximum, scaling_modifier = 0)
var/inputToDegrees = (input/inputmaximum)*180 //Converting from a 0 -> 100 scale to a 0 -> 180 scale. The 0 -> 180 scale corresponds to degrees
var/size_factor = ((-cos(inputToDegrees) +1) /2) //returns a value from 0 to 1
return size_factor + scaling_modifier //scale mod of 0 results in a number from 0 to 1. A scale modifier of +0.5 returns 0.5 to 1.5
//world<< "Transform multiplier of [src] is [size_factor + scaling_modifer]"
//converts a uniform distributed random number into a normal distributed one
//since this method produces two random numbers, one is saved for subsequent calls
//(making the cost negligble for every second call)
//This will return +/- decimals, situated about mean with standard deviation stddev
//68% chance that the number is within 1stddev
//95% chance that the number is within 2stddev
//98% chance that the number is within 3stddev...etc
var/gaussian_next
#define ACCURACY 10000
/proc/gaussian(mean, stddev)
var/R1;var/R2;var/working
if(gaussian_next != null)
R1 = gaussian_next
gaussian_next = null
else
do
R1 = rand(-ACCURACY,ACCURACY)/ACCURACY
R2 = rand(-ACCURACY,ACCURACY)/ACCURACY
working = R1*R1 + R2*R2
while(working >= 1 || working==0)
working = sqrt(-2 * log(working) / working)
R1 *= working
gaussian_next = R2 * working
return (mean + stddev * R1)
#undef ACCURACY
+153
View File
@@ -0,0 +1,153 @@
/matrix/proc/TurnTo(old_angle, new_angle)
. = new_angle - old_angle
Turn(.) //BYOND handles cases such as -270, 360, 540 etc. DOES NOT HANDLE 180 TURNS WELL, THEY TWEEN AND LOOK LIKE SHIT
/atom/proc/SpinAnimation(speed = 10, loops = -1, clockwise = 1, segments = 3)
if(!segments)
return
var/segment = 360/segments
if(!clockwise)
segment = -segment
var/list/matrices = list()
for(var/i in 1 to segments-1)
var/matrix/M = matrix(transform)
M.Turn(segment*i)
matrices += M
var/matrix/last = matrix(transform)
matrices += last
speed /= segments
animate(src, transform = matrices[1], time = speed, loops)
for(var/i in 2 to segments) //2 because 1 is covered above
animate(transform = matrices[i], time = speed)
//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
/matrix/proc/tolist()
. = list()
. += a
. += b
. += c
. += d
. += e
. += f
//Dumps the matrix data in a matrix-grid format
/*
a d 0
b e 0
c f 1
*/
/matrix/proc/togrid()
. = list()
. += a
. += d
. += 0
. += b
. += e
. += 0
. += c
. += f
. += 1
//The X pixel offset of this matrix
/matrix/proc/get_x_shift()
. = c
//The Y pixel offset of this matrix
/matrix/proc/get_y_shift()
. = f
// Color matrices:
//Luma coefficients suggested for HDTVs. If you change these, make sure they add up to 1.
#define LUMR 0.2126
#define LUMG 0.7152
#define LUMB 0.0722
//Still need color matrix addition, negation, and multiplication.
//Returns an identity color matrix which does nothing
/proc/color_identity()
return list(1,0,0, 0,1,0, 0,0,1)
//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_rotation(angle)
if(angle == 0)
return color_identity()
angle = Clamp(angle, -180, 180)
var/cos = cos(angle)
var/sin = sin(angle)
var/constA = 0.143
var/constB = 0.140
var/constC = -0.283
return list(
LUMR + cos * (1-LUMR) + sin * -LUMR, LUMR + cos * -LUMR + sin * constA, LUMR + cos * -LUMR + sin * -(1-LUMR),
LUMG + cos * -LUMG + sin * -LUMG, LUMG + cos * (1-LUMG) + sin * constB, LUMG + cos * -LUMG + sin * LUMG,
LUMB + cos * -LUMB + sin * (1-LUMB), LUMB + cos * -LUMB + sin * constC, LUMB + cos * (1-LUMB) + sin * LUMB
)
//Makes everything brighter or darker without regard to existing color or brightness
/proc/color_brightness(power)
power = Clamp(power, -255, 255)
power = power/255
return list(1,0,0, 0,1,0, 0,0,1, power,power,power)
/var/list/delta_index = list(
0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11,
0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24,
0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42,
0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68,
0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98,
1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54,
1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25,
2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8,
4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0,
7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8,
10.0)
//Exxagerates or removes brightness
/proc/color_contrast(value)
value = Clamp(value, -100, 100)
if(value == 0)
return color_identity()
var/x = 0
if (value < 0)
x = 127 + value / 100 * 127;
else
x = value % 1
if(x == 0)
x = delta_index[value]
else
x = delta_index[value] * (1-x) + delta_index[value+1] * x//use linear interpolation for more granularity.
x = x * 127 + 127
var/mult = x / 127
var/add = 0.5 * (127-x) / 255
return list(mult,0,0, 0,mult,0, 0,0,mult, add,add,add)
//Exxagerates or removes colors
/proc/color_saturation(value as num)
if(value == 0)
return color_identity()
value = Clamp(value, -100, 100)
if(value > 0)
value *= 3
var/x = 1 + value / 100
var/inv = 1 - x
var/R = LUMR * inv
var/G = LUMG * inv
var/B = LUMB * inv
return list(R + x,R,R, G,G + x,G, B,B,B + x)
#undef LUMR
#undef LUMG
#undef LUMB
+362
View File
@@ -0,0 +1,362 @@
/proc/random_blood_type()
return pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+")
/proc/random_eye_color()
switch(pick(20;"brown",20;"hazel",20;"grey",15;"blue",15;"green",1;"amber",1;"albino"))
if("brown")
return "630"
if("hazel")
return "542"
if("grey")
return pick("666","777","888","999","aaa","bbb","ccc")
if("blue")
return "36c"
if("green")
return "060"
if("amber")
return "fc0"
if("albino")
return pick("c","d","e","f") + pick("0","1","2","3","4","5","6","7","8","9") + pick("0","1","2","3","4","5","6","7","8","9")
else
return "000"
/proc/random_underwear(gender)
if(!underwear_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, underwear_list, underwear_m, underwear_f)
switch(gender)
if(MALE)
return pick(underwear_m)
if(FEMALE)
return pick(underwear_f)
else
return pick(underwear_list)
/proc/random_undershirt(gender)
if(!undershirt_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, undershirt_list, undershirt_m, undershirt_f)
switch(gender)
if(MALE)
return pick(undershirt_m)
if(FEMALE)
return pick(undershirt_f)
else
return pick(undershirt_list)
/proc/random_socks()
if(!socks_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, socks_list)
return pick(socks_list)
/proc/random_features()
if(!tails_list_human.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/human, tails_list_human)
if(!tails_list_lizard.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/lizard, tails_list_lizard)
if(!snouts_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/snouts, snouts_list)
if(!horns_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/horns, horns_list)
if(!ears_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/ears, horns_list)
if(!frills_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/frills, frills_list)
if(!spines_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/spines, spines_list)
if(!body_markings_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, body_markings_list)
if(!wings_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/wings, wings_list)
//For now we will always return none for tail_human and ears.
return(list("mcolor" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), "tail_lizard" = pick(tails_list_lizard), "tail_human" = "None", "wings" = "None", "snout" = pick(snouts_list), "horns" = pick(horns_list), "ears" = "None", "frills" = pick(frills_list), "spines" = pick(spines_list), "body_markings" = pick(body_markings_list)))
/proc/random_hair_style(gender)
switch(gender)
if(MALE)
return pick(hair_styles_male_list)
if(FEMALE)
return pick(hair_styles_female_list)
else
return pick(hair_styles_list)
/proc/random_facial_hair_style(gender)
switch(gender)
if(MALE)
return pick(facial_hair_styles_male_list)
if(FEMALE)
return pick(facial_hair_styles_female_list)
else
return pick(facial_hair_styles_list)
/proc/random_unique_name(gender, attempts_to_find_unique_name=10)
for(var/i=1, i<=attempts_to_find_unique_name, i++)
if(gender==FEMALE)
. = capitalize(pick(first_names_female)) + " " + capitalize(pick(last_names))
else
. = capitalize(pick(first_names_male)) + " " + capitalize(pick(last_names))
if(i != attempts_to_find_unique_name && !findname(.))
break
/proc/random_unique_lizard_name(gender, attempts_to_find_unique_name=10)
for(var/i=1, i<=attempts_to_find_unique_name, i++)
. = capitalize(lizard_name(gender))
if(i != attempts_to_find_unique_name && !findname(.))
break
/proc/random_skin_tone()
return pick(skin_tones)
var/list/skin_tones = list(
"albino",
"caucasian1",
"caucasian2",
"caucasian3",
"latino",
"mediterranean",
"asian1",
"asian2",
"arab",
"indian",
"african1",
"african2"
)
var/global/list/species_list[0]
var/global/list/roundstart_species[0]
/proc/age2agedescription(age)
switch(age)
if(0 to 1)
return "infant"
if(1 to 3)
return "toddler"
if(3 to 13)
return "child"
if(13 to 19)
return "teenager"
if(19 to 30)
return "young adult"
if(30 to 45)
return "adult"
if(45 to 60)
return "middle-aged"
if(60 to 70)
return "aging"
if(70 to INFINITY)
return "elderly"
else
return "unknown"
/*
Proc for attack log creation, because really why not
1 argument is the actor
2 argument is the target of action
3 is the description of action(like punched, throwed, or any other verb)
4 should it make adminlog note or not
5 is the tool with which the action was made(usually item) 5 and 6 are very similar(5 have "by " before it, that it) and are separated just to keep things in a bit more in order
6 is additional information, anything that needs to be added
*/
/proc/add_logs(mob/user, mob/target, what_done, object=null, addition=null)
var/newhealthtxt = ""
var/coordinates = ""
var/turf/attack_location = get_turf(target)
if(attack_location)
coordinates = "([attack_location.x],[attack_location.y],[attack_location.z])"
if(target && isliving(target))
var/mob/living/L = target
newhealthtxt = " (NEWHP: [L.health])"
if(user && ismob(user))
user.attack_log += text("\[[time_stamp()]\] <font color='red'>Has [what_done] [target ? "[target.name][(ismob(target) && target.ckey) ? "([target.ckey])" : ""]" : "NON-EXISTANT SUBJECT"][object ? " with [object]" : " "][addition][newhealthtxt][coordinates]</font>")
if(user.mind)
user.mind.attack_log += text("\[[time_stamp()]\] <font color='red'>[user ? "[user.name][(ismob(user) && user.ckey) ? "([user.ckey])" : ""]" : "NON-EXISTANT SUBJECT"] has [what_done] [target ? "[target.name][(ismob(target) && target.ckey) ? "([target.ckey])" : ""]" : "NON-EXISTANT SUBJECT"][object ? " with [object]" : " "][addition][newhealthtxt][coordinates]</font>")
if(target && ismob(target))
target.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been [what_done] by [user ? "[user.name][(ismob(user) && user.ckey) ? "([user.ckey])" : ""]" : "NON-EXISTANT SUBJECT"][object ? " with [object]" : " "][addition][newhealthtxt][coordinates]</font>")
if(target.mind)
target.mind.attack_log += text("\[[time_stamp()]\] <font color='orange'>[target ? "[target.name][(ismob(target) && target.ckey) ? "([target.ckey])" : ""]" : "NON-EXISTANT SUBJECT"] has been [what_done] by [user ? "[user.name][(ismob(user) && user.ckey) ? "([user.ckey])" : ""]" : "NON-EXISTANT SUBJECT"][object ? " with [object]" : " "][addition][newhealthtxt][coordinates]</font>")
log_attack("[user ? "[user.name][(ismob(user) && user.ckey) ? "([user.ckey])" : ""]" : "NON-EXISTANT SUBJECT"] [what_done] [target ? "[target.name][(ismob(target) && target.ckey)? "([target.ckey])" : ""]" : "NON-EXISTANT SUBJECT"][object ? " with [object]" : " "][addition][newhealthtxt][coordinates]")
/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1)
if(!user || !target)
return 0
var/user_loc = user.loc
var/drifting = 0
if(!user.Process_Spacemove(0) && user.inertia_dir)
drifting = 1
var/target_loc = target.loc
var/holding = user.get_active_hand()
var/datum/progressbar/progbar
if (progress)
progbar = new(user, time, target)
var/endtime = world.time+time
var/starttime = world.time
. = 1
while (world.time < endtime)
stoplag()
if (progress)
progbar.update(world.time - starttime)
if(!user || !target)
. = 0
break
if(uninterruptible)
continue
if(drifting && !user.inertia_dir)
drifting = 0
user_loc = user.loc
if((!drifting && user.loc != user_loc) || target.loc != target_loc || user.get_active_hand() != holding || user.incapacitated() || user.lying )
. = 0
break
if (progress)
qdel(progbar)
/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1)
if(!user)
return 0
var/atom/Tloc = null
if(target)
Tloc = target.loc
var/atom/Uloc = user.loc
var/drifting = 0
if(!user.Process_Spacemove(0) && user.inertia_dir)
drifting = 1
var/holding = user.get_active_hand()
var/holdingnull = 1 //User's hand started out empty, check for an empty hand
if(holding)
holdingnull = 0 //Users hand started holding something, check to see if it's still holding that
var/datum/progressbar/progbar
if (progress)
progbar = new(user, delay, target)
var/endtime = world.time + delay
var/starttime = world.time
. = 1
while (world.time < endtime)
stoplag()
if (progress)
progbar.update(world.time - starttime)
if(drifting && !user.inertia_dir)
drifting = 0
Uloc = user.loc
if(!user || user.stat || user.weakened || user.stunned || (!drifting && user.loc != Uloc))
. = 0
break
if(Tloc && (!target || Tloc != target.loc))
. = 0
break
if(needhand)
//This might seem like an odd check, but you can still need a hand even when it's empty
//i.e the hand is used to pull some item/tool out of the construction
if(!holdingnull)
if(!holding)
. = 0
break
if(user.get_active_hand() != holding)
. = 0
break
if (progress)
qdel(progbar)
/proc/do_after_mob(mob/user, var/list/targets, time = 30, uninterruptible = 0, progress = 1)
if(!user || !targets)
return 0
if(!islist(targets))
targets = list(targets)
var/user_loc = user.loc
var/drifting = 0
if(!user.Process_Spacemove(0) && user.inertia_dir)
drifting = 1
var/list/originalloc = list()
for(var/atom/target in targets)
originalloc[target] = target.loc
var/holding = user.get_active_hand()
var/datum/progressbar/progbar
if(progress)
progbar = new(user, time, targets[1])
var/endtime = world.time + time
var/starttime = world.time
. = 1
mainloop:
while(world.time < endtime)
sleep(1)
if(progress)
progbar.update(world.time - starttime)
if(!user || !targets)
. = 0
break
if(uninterruptible)
continue
if(drifting && !user.inertia_dir)
drifting = 0
user_loc = user.loc
for(var/atom/target in targets)
if((!drifting && user_loc != user.loc) || originalloc[target] != target.loc || user.get_active_hand() != holding || user.incapacitated() || user.lying )
. = 0
break mainloop
if(progbar)
qdel(progbar)
/proc/is_species(A, species_datum)
. = FALSE
if(ishuman(A))
var/mob/living/carbon/human/H = A
if(H.dna && istype(H.dna.species, species_datum))
. = TRUE
/proc/deadchat_broadcast(message, mob/follow_target=null, speaker_key=null, message_type=DEADCHAT_REGULAR)
for(var/mob/M in player_list)
var/datum/preferences/prefs
if(M.client && M.client.prefs)
prefs = M.client.prefs
else
prefs = new
var/adminoverride = 0
if(M.client && M.client.holder && (prefs.chat_toggles & CHAT_DEAD))
adminoverride = 1
if(istype(M, /mob/new_player) && !adminoverride)
continue
if(M.stat != DEAD && !adminoverride)
continue
if(speaker_key && speaker_key in prefs.ignoring)
continue
switch(message_type)
if(DEADCHAT_DEATHRATTLE)
if(prefs.toggles & DISABLE_DEATHRATTLE)
continue
if(DEADCHAT_ARRIVALRATTLE)
if(prefs.toggles & DISABLE_ARRIVALRATTLE)
continue
if(istype(M, /mob/dead/observer) && follow_target)
var/link = FOLLOW_LINK(M, follow_target)
M << "[link] [message]"
else
M << "[message]"
+225
View File
@@ -0,0 +1,225 @@
/proc/lizard_name(gender)
if(gender == MALE)
return "[pick(lizard_names_male)]-[pick(lizard_names_male)]"
else
return "[pick(lizard_names_female)]-[pick(lizard_names_female)]"
var/church_name = null
/proc/church_name()
if (church_name)
return church_name
var/name = ""
name += pick("Holy", "United", "First", "Second", "Last")
if (prob(20))
name += " Space"
name += " " + pick("Church", "Cathedral", "Body", "Worshippers", "Movement", "Witnesses")
name += " of [religion_name()]"
return name
var/command_name = null
/proc/command_name()
if (command_name)
return command_name
var/name = "Central Command"
command_name = name
return name
/proc/change_command_name(name)
command_name = name
return name
var/religion_name = null
/proc/religion_name()
if (religion_name)
return religion_name
var/name = ""
name += pick("bee", "science", "edu", "captain", "assistant", "monkey", "alien", "space", "unit", "sprocket", "gadget", "bomb", "revolution", "beyond", "station", "goon", "robot", "ivor", "hobnob")
name += pick("ism", "ia", "ology", "istism", "ites", "ick", "ian", "ity")
return capitalize(name)
/proc/station_name()
if(station_name)
return station_name
if(config && config.station_name)
station_name = config.station_name
else
station_name = new_station_name()
if(config && config.server_name)
world.name = "[config.server_name][config.server_name==station_name ? "" : ": [station_name]"]"
else
world.name = station_name
return station_name
/proc/new_station_name()
var/random = rand(1,5)
var/name = ""
var/new_station_name = ""
//Rare: Pre-Prefix
if (prob(10))
name = pick(station_prefixes)
new_station_name = name + " "
name = ""
// Prefix
for(var/holiday_name in SSevent.holidays)
if(holiday_name == "Friday the 13th")
random = 13
var/datum/holiday/holiday = SSevent.holidays[holiday_name]
name = holiday.getStationPrefix()
//get normal name
if(!name)
name = pick(station_names)
if(name)
new_station_name += name + " "
// Suffix
name = pick(station_suffixes)
new_station_name += name + " "
// ID Number
switch(random)
if(1)
new_station_name += "[rand(1, 99)]"
if(2)
new_station_name += pick(greek_letters)
if(3)
new_station_name += pick(roman_numerals)
if(4)
new_station_name += pick(phonetic_alphabet)
if(5)
new_station_name += pick(numbers_as_words)
if(13)
new_station_name += pick("13","XIII","Thirteen")
return new_station_name
var/syndicate_name = null
/proc/syndicate_name()
if (syndicate_name)
return syndicate_name
var/name = ""
// Prefix
name += pick("Clandestine", "Prima", "Blue", "Zero-G", "Max", "Blasto", "Waffle", "North", "Omni", "Newton", "Cyber", "Bonk", "Gene", "Gib")
// Suffix
if (prob(80))
name += " "
// Full
if (prob(60))
name += pick("Syndicate", "Consortium", "Collective", "Corporation", "Group", "Holdings", "Biotech", "Industries", "Systems", "Products", "Chemicals", "Enterprises", "Family", "Creations", "International", "Intergalactic", "Interplanetary", "Foundation", "Positronics", "Hive")
// Broken
else
name += pick("Syndi", "Corp", "Bio", "System", "Prod", "Chem", "Inter", "Hive")
name += pick("", "-")
name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Code")
// Small
else
name += pick("-", "*", "")
name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Gen", "Star", "Dyne", "Code", "Hive")
syndicate_name = name
return name
//Traitors and traitor silicons will get these. Revs will not.
var/syndicate_code_phrase//Code phrase for traitors.
var/syndicate_code_response//Code response for traitors.
/*
Should be expanded.
How this works:
Instead of "I'm looking for James Smith," the traitor would say "James Smith" as part of a conversation.
Another traitor may then respond with: "They enjoy running through the void-filled vacuum of the derelict."
The phrase should then have the words: James Smith.
The response should then have the words: run, void, and derelict.
This way assures that the code is suited to the conversation and is unpredicatable.
Obviously, some people will be better at this than others but in theory, everyone should be able to do it and it only enhances roleplay.
Can probably be done through "{ }" but I don't really see the practical benefit.
One example of an earlier system is commented below.
/N
*/
/proc/generate_code_phrase()//Proc is used for phrase and response in master_controller.dm
var/code_phrase = ""//What is returned when the proc finishes.
var/words = pick(//How many words there will be. Minimum of two. 2, 4 and 5 have a lesser chance of being selected. 3 is the most likely.
50; 2,
200; 3,
50; 4,
25; 5
)
var/safety[] = list(1,2,3)//Tells the proc which options to remove later on.
var/nouns[] = list("love","hate","anger","peace","pride","sympathy","bravery","loyalty","honesty","integrity","compassion","charity","success","courage","deceit","skill","beauty","brilliance","pain","misery","beliefs","dreams","justice","truth","faith","liberty","knowledge","thought","information","culture","trust","dedication","progress","education","hospitality","leisure","trouble","friendships", "relaxation")
var/drinks[] = list("vodka and tonic","gin fizz","bahama mama","manhattan","black Russian","whiskey soda","long island tea","margarita","Irish coffee"," manly dwarf","Irish cream","doctor's delight","Beepksy Smash","tequila sunrise","brave bull","gargle blaster","bloody mary","whiskey cola","white Russian","vodka martini","martini","Cuba libre","kahlua","vodka","wine","moonshine")
var/locations[] = teleportlocs.len ? teleportlocs : drinks//if null, defaults to drinks instead.
var/names[] = list()
for(var/datum/data/record/t in data_core.general)//Picks from crew manifest.
names += t.fields["name"]
var/maxwords = words//Extra var to check for duplicates.
for(words,words>0,words--)//Randomly picks from one of the choices below.
if(words==1&&(1 in safety)&&(2 in safety))//If there is only one word remaining and choice 1 or 2 have not been selected.
safety = list(pick(1,2))//Select choice 1 or 2.
else if(words==1&&maxwords==2)//Else if there is only one word remaining (and there were two originally), and 1 or 2 were chosen,
safety = list(3)//Default to list 3
switch(pick(safety))//Chance based on the safety list.
if(1)//1 and 2 can only be selected once each to prevent more than two specific names/places/etc.
switch(rand(1,2))//Mainly to add more options later.
if(1)
if(names.len&&prob(70))
code_phrase += pick(names)
else
if(prob(10))
code_phrase += pick(lizard_name(MALE),lizard_name(FEMALE))
else
code_phrase += pick(pick(first_names_male,first_names_female))
code_phrase += " "
code_phrase += pick(last_names)
if(2)
code_phrase += pick(get_all_jobs())//Returns a job.
safety -= 1
if(2)
switch(rand(1,2))//Places or things.
if(1)
code_phrase += pick(drinks)
if(2)
code_phrase += pick(locations)
safety -= 2
if(3)
switch(rand(1,3))//Nouns, adjectives, verbs. Can be selected more than once.
if(1)
code_phrase += pick(nouns)
if(2)
code_phrase += pick(adjectives)
if(3)
code_phrase += pick(verbs)
if(words==1)
code_phrase += "."
else
code_phrase += ", "
return code_phrase
+14
View File
@@ -0,0 +1,14 @@
// Ensure the frequency is within bounds of what it should be sending/recieving at
/proc/sanitize_frequency(frequency, free = FALSE)
. = round(frequency)
if(free)
. = Clamp(frequency, MIN_FREE_FREQ, MAX_FREE_FREQ)
else
. = Clamp(frequency, MIN_FREQ, MAX_FREQ)
if(!(. % 2)) // Ensure the last digit is an odd number
. += 1
// Format frequency by moving the decimal.
/proc/format_frequency(frequency)
frequency = text2num(frequency)
return "[round(frequency / 10)].[frequency % 10]"
+74
View File
@@ -0,0 +1,74 @@
//general stuff
/proc/sanitize_integer(number, min=0, max=1, default=0)
if(isnum(number))
number = round(number)
if(min <= number && number <= max)
return number
return default
/proc/sanitize_text(text, default="")
if(istext(text))
return text
return default
/proc/sanitize_inlist(value, list/List, default)
if(value in List)
return value
if(default)
return default
if(List && List.len)
return pick(List)
//more specialised stuff
/proc/sanitize_gender(gender,neuter=0,plural=0, default="male")
switch(gender)
if(MALE, FEMALE)
return gender
if(NEUTER)
if(neuter)
return gender
else
return default
if(PLURAL)
if(plural)
return gender
else
return default
return default
/proc/sanitize_hexcolor(color, desired_format=3, include_crunch=0, default)
var/crunch = include_crunch ? "#" : ""
if(!istext(color))
color = ""
var/start = 1 + (text2ascii(color,1)==35)
var/len = length(color)
var/step_size = 1 + ((len+1)-start != desired_format)
. = ""
for(var/i=start, i<=len, i+=step_size)
var/ascii = text2ascii(color,i)
switch(ascii)
if(48 to 57)
. += ascii2text(ascii) //numbers 0 to 9
if(97 to 102)
. += ascii2text(ascii) //letters a to f
if(65 to 70)
. += ascii2text(ascii+32) //letters A to F - translates to lowercase
else
break
if(length(.) != desired_format)
if(default)
return default
return crunch + repeat_string(desired_format, "0")
return crunch + .
/proc/sanitize_ooccolor(color)
var/list/HSL = rgb2hsl(hex2num(copytext(color,2,4)),hex2num(copytext(color,4,6)),hex2num(copytext(color,6,8)))
HSL[3] = min(HSL[3],0.4)
var/list/RGB = hsl2rgb(arglist(HSL))
return "#[num2hex(RGB[1],2)][num2hex(RGB[2],2)][num2hex(RGB[3],2)]"
+16
View File
@@ -0,0 +1,16 @@
//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)
if(L && L.len >= 2)
fromIndex = fromIndex % L.len
toIndex = toIndex % (L.len+1)
if(fromIndex <= 0)
fromIndex += L.len
if(toIndex <= 0)
toIndex += L.len + 1
sortInstance.L = L
sortInstance.cmp = cmp
sortInstance.associative = associative
sortInstance.binarySort(fromIndex, toIndex, fromIndex)
return L
+16
View File
@@ -0,0 +1,16 @@
//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)
if(L && L.len >= 2)
fromIndex = fromIndex % L.len
toIndex = toIndex % (L.len+1)
if(fromIndex <= 0)
fromIndex += L.len
if(toIndex <= 0)
toIndex += L.len + 1
sortInstance.L = L
sortInstance.cmp = cmp
sortInstance.associative = associative
sortInstance.mergeSort(fromIndex, toIndex)
return L
+17
View File
@@ -0,0 +1,17 @@
//TimSort interface
/proc/sortTim(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)
if(fromIndex <= 0)
fromIndex += L.len
if(toIndex <= 0)
toIndex += L.len + 1
sortInstance.L = L
sortInstance.cmp = cmp
sortInstance.associative = associative
sortInstance.timSort(fromIndex, toIndex)
return L
+656
View File
@@ -0,0 +1,656 @@
//These are macros used to reduce on proc calls
#define fetchElement(L, i) (associative) ? L[L[i]] : L[i]
//Minimum sized sequence that will be merged. Anything smaller than this will use binary-insertion sort.
//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.
#define MIN_GALLOP 7
//This is a global instance to allow much of this code to be reused. The interfaces are kept seperately
var/datum/sortInstance/sortInstance = new()
/datum/sortInstance
//The array being sorted.
var/list/L
//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]])
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
//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/stackSize
var/list/runBases = list()
var/list/runLens = list()
proc/timSort(start, end)
runBases.Cut()
runLens.Cut()
var/remaining = end - start
//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)
return
//March over the array finding natural runs
//Extend any short natural runs to runs of length minRun
var/minRun = minRunLength(remaining)
do
//identify next run
var/runLen = countRunAndMakeAscending(start, end)
//if run is short, extend to min(minRun, remaining)
if(runLen < minRun)
var/force = (remaining <= minRun) ? remaining : minRun
binarySort(start, start+force, start+runLen)
runLen = force
//add data about run to queue
runBases.Add(start)
runLens.Add(runLen)
//maybe merge
mergeCollapse()
//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)
//reset minGallop, for successive calls
minGallop = 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
*/
proc/binarySort(lo, hi, start)
//ASSERT(lo <= start && start <= hi)
if(start <= lo)
start = lo + 1
for(,start < hi, ++start)
var/pivot = fetchElement(L,start)
//set left and right to the index where pivot belongs
var/left = lo
var/right = start
//ASSERT(left <= right)
//[lo, left) elements <= pivot < [right, start) elements
//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)
right = mid
else
left = mid+1
//ASSERT(left == right)
moveElement(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.
*/
proc/countRunAndMakeAscending(lo, hi)
//ASSERT(lo < hi)
var/runHi = lo + 1
if(runHi >= hi)
return 1
var/last = fetchElement(L,lo)
var/current = fetchElement(L,runHi++)
if(call(cmp)(current, last) < 0)
while(runHi < hi)
last = current
current = fetchElement(L,runHi)
if(call(cmp)(current, last) >= 0)
break
++runHi
reverseRange(L, lo, runHi)
else
while(runHi < hi)
last = current
current = fetchElement(L,runHi)
if(call(cmp)(current, last) < 0)
break
++runHi
return runHi - lo
//Returns the minimum acceptable run length for an array of the specified length.
//Natural runs shorter than this will be extended with binarySort
proc/minRunLength(n)
//ASSERT(n >= 0)
var/r = 0 //becomes 1 if any bits are shifted off
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
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])
--n
mergeAt(n)
else if(runLens[n] <= runLens[n+1])
mergeAt(n)
else
break //Invariant is established
//Merges all runs on the stack until only one remains.
//Called only once, to finalise the sort
proc/mergeForceCollapse()
while(runBases.len >= 2)
var/n = runBases.len - 1
if(n > 1 && runLens[n-1] < runLens[n+1])
--n
mergeAt(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
proc/mergeAt(i)
//ASSERT(runBases.len >= 2)
//ASSERT(i >= 1)
//ASSERT(i == runBases.len - 1 || i == runBases.len - 2)
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)
//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)
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)
if(len2 == 0)
return
//Merge remaining runs, using tmp array with min(len1, len2) elements
if(len1 <= len2)
mergeLo(base1, len1, base2, len2)
else
mergeHi(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'
*/
proc/gallopLeft(key, base, len, hint)
//ASSERT(len > 0 && hint >= 0 && hint < len)
var/lastOffset = 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
offset = (offset << 1) + 1
if(offset > maxOffset)
offset = maxOffset
lastOffset += hint
offset += hint
else
var/maxOffset = hint + 1
while(offset < maxOffset && call(cmp)(key, fetchElement(L,base+hint-offset)) <= 0)
lastOffset = offset
offset = (offset << 1) + 1
if(offset > maxOffset)
offset = maxOffset
var/temp = lastOffset
lastOffset = hint - offset
offset = hint - temp
//ASSERT(-1 <= lastOffset && lastOffset < 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)
if(call(cmp)(key, fetchElement(L,base+m)) > 0)
lastOffset = m + 1
else
offset = m
//ASSERT(lastOffset == 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.
*
* @param key the key whose insertion point to search for
* @param a the array in which to search
* @param base the index of the first element in the range
* @param len the length of the range; must be > 0
* @param hint the index at which to begin the search, 0 <= hint < n.
* The closer hint is to the result, the faster this method will run.
* @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]
*/
proc/gallopRight(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 <= 0) //int overflow, not an issue here since we are using floats
// offset = maxOffset
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
offset = (offset << 1) + 1
//if(offset <= 0) //int overflow, not an issue here since we are using floats
// offset = maxOffset
if(offset > maxOffset)
offset = maxOffset
lastOffset += hint
offset += hint
//ASSERT(-1 <= lastOffset && lastOffset < offset && offset <= len)
++lastOffset
while(lastOffset < offset)
var/m = lastOffset + ((offset - lastOffset) >> 1)
if(call(cmp)(key, fetchElement(L,base+m)) < 0) //key <= L[base+m]
offset = m
else //key > L[base+m]
lastOffset = m + 1
//ASSERT(lastOffset == offset)
return offset
//Merges two adjacent runs in-place in a stable fashion.
//For performance this method should only be called when len1 <= len2!
proc/mergeLo(base1, len1, base2, len2)
//ASSERT(len1 > 0 && len2 > 0 && base1 + len1 == base2)
var/cursor1 = base1
var/cursor2 = base2
//degenerate cases
if(len2 == 1)
moveElement(L, cursor2, cursor1)
return
if(len1 == 1)
moveElement(L, cursor1, cursor2+len2)
return
//Move first element of second run
moveElement(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
do
//ASSERT(len1 > 1 && len2 > 0)
if(call(cmp)(fetchElement(L,cursor2), fetchElement(L,cursor1)) < 0)
moveElement(L, cursor2++, cursor1++)
--len2
++count2
count1 = 0
if(len2 == 0)
break outer
else
++cursor1
++count1
count2 = 0
if(--len1 == 1)
break outer
while((count1 | count2) < minGallop)
//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)
count1 = gallopRight(fetchElement(L,cursor2), cursor1, len1, 0)
if(count1)
cursor1 += count1
len1 -= count1
if(len1 <= 1)
break outer
moveElement(L, cursor2, cursor1)
++cursor2
++cursor1
if(--len2 == 0)
break outer
count2 = gallopLeft(fetchElement(L,cursor1), cursor2, len2, 0)
if(count2)
moveRange(L, cursor2, cursor1, count2)
cursor2 += count2
cursor1 += count2
len2 -= count2
if(len2 == 0)
break outer
++cursor1
if(--len1 == 1)
break outer
--minGallop
while((count1|count2) > MIN_GALLOP)
if(minGallop < 0)
minGallop = 0
minGallop += 2; // Penalize for leaving gallop mode
if(len1 == 1)
//ASSERT(len2 > 0)
moveElement(L, cursor1, cursor2+len2)
//else
//ASSERT(len2 == 0)
//ASSERT(len1 > 1)
proc/mergeHi(base1, len1, base2, len2)
//ASSERT(len1 > 0 && len2 > 0 && base1 + len1 == base2)
var/cursor1 = base1 + len1 - 1 //start at end of sublists
var/cursor2 = base2 + len2 - 1
//degenerate cases
if(len2 == 1)
moveElement(L, base2, base1)
return
if(len1 == 1)
moveElement(L, base1, cursor2+1)
return
moveElement(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
//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)
--len1
++count1
count2 = 0
if(len1 == 0)
break outer
else
--cursor2
--len2
++count2
count1 = 0
if(len2 == 1)
break outer
while((count1 | count2) < minGallop)
//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)
count1 = len1 - gallopRight(fetchElement(L,cursor2), base1, len1, len1-1) //should cursor1 be base1?
if(count1)
cursor1 -= count1
moveRange(L, cursor1+1, cursor2+1, count1) //cursor1+1 == cursor2 by definition
cursor2 -= count1
len1 -= count1
if(len1 == 0)
break outer
--cursor2
if(--len2 == 1)
break outer
count2 = len2 - gallopLeft(fetchElement(L,cursor1), cursor1+1, len2, len2-1)
if(count2)
cursor2 -= count2
len2 -= count2
if(len2 <= 1)
break outer
moveElement(L, cursor1--, cursor2-- + 1)
--len1
if(len1 == 0)
break outer
--minGallop
while((count1|count2) > MIN_GALLOP)
if(minGallop < 0)
minGallop = 0
minGallop += 2 // Penalize for leaving gallop mode
if(len2 == 1)
//ASSERT(len1 > 0)
cursor1 -= len1
moveRange(L, cursor1+1, cursor2+1, len1)
//else
//ASSERT(len1 == 0)
//ASSERT(len2 > 0)
proc/mergeSort(start, end)
var/remaining = end - start
//If array is small, do an insertion sort
if(remaining < MIN_MERGE)
//var/initRunLen = countRunAndMakeAscending(start, end)
binarySort(start, end, start/*+initRunLen*/)
return
var/minRun = minRunLength(remaining)
do
var/runLen = (remaining <= minRun) ? remaining : minRun
binarySort(start, start+runLen, start)
//add data about run to queue
runBases.Add(start)
runLens.Add(runLen)
//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])
--n
mergeAt2(n)
else if(runLens[n] <= runLens[n+1])
mergeAt2(n)
else
break //Invariant is established
while(runBases.len >= 2)
var/n = runBases.len - 1
if(n > 1 && runLens[n-1] < runLens[n+1])
--n
mergeAt2(n)
return L
proc/mergeAt2(i)
var/cursor1 = runBases[i]
var/cursor2 = runBases[i+1]
var/end1 = cursor1+runLens[i]
var/end2 = cursor2+runLens[i+1]
var/val1 = fetchElement(L,cursor1)
var/val2 = fetchElement(L,cursor2)
while(1)
if(call(cmp)(val1,val2) < 0)
if(++cursor1 >= end1)
break
val1 = fetchElement(L,cursor1)
else
moveElement(L,cursor2,cursor1)
++cursor2
if(++cursor2 >= end2)
break
++end1
++cursor1
//if(++cursor1 >= end1)
// break
val2 = fetchElement(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)
#undef MIN_GALLOP
#undef MIN_MERGE
#undef fetchElement
+471
View File
@@ -0,0 +1,471 @@
/*
* Holds procs designed to help with filtering text
* Contains groups:
* SQL sanitization/formating
* Text sanitization
* Text searches
* Text modification
* Misc
*/
/*
* SQL sanitization
*/
// Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts.
/proc/sanitizeSQL(t as text)
var/sqltext = dbcon.Quote(t);
return copytext(sqltext, 2, lentext(sqltext));//Quote() adds quotes around input, we already do that
/proc/format_table_name(table as text)
return sqlfdbktableprefix + table
/*
* Text sanitization
*/
//Simply removes < and > and limits the length of the message
/proc/strip_html_simple(t,limit=MAX_MESSAGE_LEN)
var/list/strip_chars = list("<",">")
t = copytext(t,1,limit)
for(var/char in strip_chars)
var/index = findtext(t, char)
while(index)
t = copytext(t, 1, index) + copytext(t, index+1)
index = findtext(t, char)
return t
//Removes a few problematic characters
/proc/sanitize_simple(t,list/repl_chars = list("\n"="#","\t"="#"))
for(var/char in repl_chars)
var/index = findtext(t, char)
while(index)
t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+1)
index = findtext(t, char, index+1)
return t
//Runs byond's sanitization proc along-side sanitize_simple
/proc/sanitize(t,list/repl_chars = null)
return html_encode(sanitize_simple(t,repl_chars))
//Runs sanitize and strip_html_simple
//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '&lt;' after sanitize() calls byond's html_encode()
/proc/strip_html(t,limit=MAX_MESSAGE_LEN)
return copytext((sanitize(strip_html_simple(t))),1,limit)
//Runs byond's sanitization proc along-side strip_html_simple
//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '&lt;' that html_encode() would cause
/proc/adminscrub(t,limit=MAX_MESSAGE_LEN)
return copytext((html_encode(strip_html_simple(t))),1,limit)
//Returns null if there is any bad text in the string
/proc/reject_bad_text(text, max_length=512)
if(length(text) > max_length)
return //message too long
var/non_whitespace = 0
for(var/i=1, i<=length(text), i++)
switch(text2ascii(text,i))
if(62,60,92,47)
return //rejects the text if it contains these bad characters: <, >, \ or /
if(127 to 255)
return //rejects weird letters like
if(0 to 31)
return //more weird stuff
if(32)
continue //whitespace
else
non_whitespace = 1
if(non_whitespace)
return text //only accepts the text if it has some non-spaces
// Used to get a properly sanitized input, of max_length
/proc/stripped_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN)
var/name = input(user, message, title, default) as text|null
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
/proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN)
var/name = input(user, message, title, default) as message|null
return html_encode(trim(name, max_length))
//Filters out undesirable characters from names
/proc/reject_bad_name(t_in, allow_numbers=0, max_length=MAX_NAME_LEN)
if(!t_in || length(t_in) > max_length)
return //Rejects the input if it is null or if it is longer then the max length allowed
var/number_of_alphanumeric = 0
var/last_char_group = 0
var/t_out = ""
for(var/i=1, i<=length(t_in), i++)
var/ascii_char = text2ascii(t_in,i)
switch(ascii_char)
// A .. Z
if(65 to 90) //Uppercase Letters
t_out += ascii2text(ascii_char)
number_of_alphanumeric++
last_char_group = 4
// a .. z
if(97 to 122) //Lowercase Letters
if(last_char_group<2)
t_out += ascii2text(ascii_char-32) //Force uppercase first character
else
t_out += ascii2text(ascii_char)
number_of_alphanumeric++
last_char_group = 4
// 0 .. 9
if(48 to 57) //Numbers
if(!last_char_group)
continue //suppress at start of string
if(!allow_numbers)
continue
t_out += ascii2text(ascii_char)
number_of_alphanumeric++
last_char_group = 3
// ' - .
if(39,45,46) //Common name punctuation
if(!last_char_group)
continue
t_out += ascii2text(ascii_char)
last_char_group = 2
// ~ | @ : # $ % & * +
if(126,124,64,58,35,36,37,38,42,43) //Other symbols that we'll allow (mainly for AI)
if(!last_char_group)
continue //suppress at start of string
if(!allow_numbers)
continue
t_out += ascii2text(ascii_char)
last_char_group = 2
//Space
if(32)
if(last_char_group <= 1)
continue //suppress double-spaces and spaces at start of string
t_out += ascii2text(ascii_char)
last_char_group = 1
else
return
if(number_of_alphanumeric < 2)
return //protects against tiny names like "A" and also names like "' ' ' ' ' ' ' '"
if(last_char_group == 1)
t_out = copytext(t_out,1,length(t_out)) //removes the last character (in this case a space)
for(var/bad_name in list("space","floor","wall","r-wall","monkey","unknown","inactive ai")) //prevents these common metagamey names
if(cmptext(t_out,bad_name))
return //(not case sensitive)
return t_out
//html_encode helper proc that returns the smallest non null of two numbers
//or 0 if they're both null (needed because of findtext returning 0 when a value is not present)
/proc/non_zero_min(a, b)
if(!a)
return b
if(!b)
return a
return (a < b ? a : b)
/*
* Text searches
*/
//Checks the beginning of a string for a specified sub-string
//Returns the position of the substring or 0 if it was not found
/proc/dd_hasprefix(text, prefix)
var/start = 1
var/end = length(prefix) + 1
return findtext(text, prefix, start, end)
//Checks the beginning of a string for a specified sub-string. This proc is case sensitive
//Returns the position of the substring or 0 if it was not found
/proc/dd_hasprefix_case(text, prefix)
var/start = 1
var/end = length(prefix) + 1
return findtextEx(text, prefix, start, end)
//Checks the end of a string for a specified substring.
//Returns the position of the substring or 0 if it was not found
/proc/dd_hassuffix(text, suffix)
var/start = length(text) - length(suffix)
if(start)
return findtext(text, suffix, start, null)
return
//Checks the end of a string for a specified substring. This proc is case sensitive
//Returns the position of the substring or 0 if it was not found
/proc/dd_hassuffix_case(text, suffix)
var/start = length(text) - length(suffix)
if(start)
return findtextEx(text, suffix, start, null)
//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'
/proc/add_lspace(t, u)
while(length(t) < u)
t = " [t]"
return 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
/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
/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.
/proc/trim(text, max_length)
if(max_length)
text = copytext(text, 1, max_length)
return trim_left(trim_right(text))
//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)
//Centers text by adding spaces to either side of the string.
/proc/dd_centertext(message, length)
var/new_message = message
var/size = length(message)
var/delta = length - size
if(size == length)
return new_message
if(size > length)
return copytext(new_message, 1, length + 1)
if(delta == 1)
return new_message + " "
if(delta % 2)
new_message = " " + new_message
delta--
var/spaces = add_lspace("",delta/2-1)
return spaces + new_message + spaces
//Limits the length of the text. Note: MAX_MESSAGE_LEN and MAX_NAME_LEN are widely used for this purpose
/proc/dd_limittext(message, length)
var/size = length(message)
if(size <= length)
return message
return copytext(message, 1, length + 1)
/proc/stringmerge(text,compare,replace = "*")
//This proc fills in all spaces with the "replace" var (* by default) with whatever
//is in the other string at the same spot (assuming it is not a replace char).
//This is used for fingerprints
var/newtext = text
if(lentext(text) != lentext(compare))
return 0
for(var/i = 1, i < lentext(text), i++)
var/a = copytext(text,i,i+1)
var/b = copytext(compare,i,i+1)
//if it isn't both the same letter, or if they are both the replacement character
//(no way to know what it was supposed to be)
if(a != b)
if(a == replace) //if A is the replacement char
newtext = copytext(newtext,1,i) + b + copytext(newtext, i+1)
else if(b == replace) //if B is the replacement char
newtext = copytext(newtext,1,i) + a + copytext(newtext, i+1)
else //The lists disagree, Uh-oh!
return 0
return newtext
/proc/stringpercent(text,character = "*")
//This proc returns the number of chars of the string that is the character
//This is used for detective work to determine fingerprint completion.
if(!text || !character)
return 0
var/count = 0
for(var/i = 1, i <= lentext(text), i++)
var/a = copytext(text,i,i+1)
if(a == character)
count++
return count
/proc/reverse_text(text = "")
var/new_text = ""
for(var/i = length(text); i > 0; i--)
new_text += copytext(text, i, i+1)
return new_text
var/list/zero_character_only = list("0")
var/list/hex_characters = list("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f")
var/list/alphabet = list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z")
var/list/binary = list("0","1")
/proc/random_string(length, list/characters)
. = ""
for(var/i=1, i<=length, i++)
. += pick(characters)
/proc/repeat_string(times, string="")
. = ""
for(var/i=1, i<=times, i++)
. += string
/proc/random_short_color()
return random_string(3, hex_characters)
/proc/random_color()
return random_string(6, hex_characters)
/proc/add_zero2(t, u)
var/temp1
while (length(t) < u)
t = "0[t]"
temp1 = t
if (length(t) > u)
temp1 = copytext(t,2,u+1)
return temp1
//merges non-null characters (3rd argument) from "from" into "into". Returns result
//e.g. into = "Hello World"
// from = "Seeya______"
// returns"Seeya World"
//The returned text is always the same length as into
//This was coded to handle DNA gene-splicing.
/proc/merge_text(into, from, null_char="_")
. = ""
if(!istext(into))
into = ""
if(!istext(from))
from = ""
var/null_ascii = istext(null_char) ? text2ascii(null_char,1) : null_char
var/previous = 0
var/start = 1
var/end = length(into) + 1
for(var/i=1, i<end, i++)
var/ascii = text2ascii(from, i)
if(ascii == null_ascii)
if(previous != 1)
. += copytext(from, start, i)
start = i
previous = 1
else
if(previous != 0)
. += copytext(into, start, i)
start = i
previous = 0
if(previous == 0)
. += copytext(from, start, end)
else
. += copytext(into, start, end)
//finds the first occurrence of one of the characters from needles argument inside haystack
//it may appear this can be optimised, but it really can't. findtext() is so much faster than anything you can do in byondcode.
//stupid byond :(
/proc/findchar(haystack, needles, start=1, end=0)
var/temp
var/len = length(needles)
for(var/i=1, i<=len, i++)
temp = findtextEx(haystack, ascii2text(text2ascii(needles,i)), start, end) //Note: ascii2text(text2ascii) is faster than copytext()
if(temp)
end = temp
return end
/proc/parsepencode(t, mob/user=null, signfont=SIGNFONT)
if(length(t) < 1) //No input means nothing needs to be parsed
return
t = replacetext(t, "\[center\]", "<center>")
t = replacetext(t, "\[/center\]", "</center>")
t = replacetext(t, "\[br\]", "<BR>")
t = replacetext(t, "\[b\]", "<B>")
t = replacetext(t, "\[/b\]", "</B>")
t = replacetext(t, "\[i\]", "<I>")
t = replacetext(t, "\[/i\]", "</I>")
t = replacetext(t, "\[u\]", "<U>")
t = replacetext(t, "\[/u\]", "</U>")
t = replacetext(t, "\[large\]", "<font size=\"4\">")
t = replacetext(t, "\[/large\]", "</font>")
if(user)
t = replacetext(t, "\[sign\]", "<font face=\"[signfont]\"><i>[user.real_name]</i></font>")
else
t = replacetext(t, "\[sign\]", "")
t = replacetext(t, "\[field\]", "<span class=\"paper_field\"></span>")
t = replacetext(t, "\[*\]", "<li>")
t = replacetext(t, "\[hr\]", "<HR>")
t = replacetext(t, "\[small\]", "<font size = \"1\">")
t = replacetext(t, "\[/small\]", "</font>")
t = replacetext(t, "\[list\]", "<ul>")
t = replacetext(t, "\[/list\]", "</ul>")
return t
/proc/char_split(t)
. = list()
for(var/x in 1 to length(t))
. += copytext(t,x,x+1)
var/list/rot13_lookup = list()
/proc/generate_rot13_lookup()
var/letters = alphabet.Copy()
for(var/c in alphabet)
letters += uppertext(c)
for(var/char in letters)
var/ascii_char = text2ascii(char, 1)
var/index
switch(ascii_char)
// A - Z
if(65 to 90)
index = 65
// a - z
if(97 to 122)
index = 97
var/d = ascii_char - index
d += 13
if(d >= 26)
d -= 26
ascii_char = index + d
var/translated_char = ascii2text(ascii_char)
rot13_lookup[char] = translated_char
/proc/rot13(t_in)
if(!rot13_lookup.len)
generate_rot13_lookup()
var/t_out = ""
for(var/i in 1 to length(t_in))
var/char = copytext(t_in, i, i + 1)
if(char in rot13_lookup)
t_out += rot13_lookup[char]
else
t_out += char
return t_out
+25
View File
@@ -0,0 +1,25 @@
//Returns the world time in english
/proc/worldtime2text()
return gameTimestamp("hh:mm")
/proc/time_stamp(format = "hh:mm:ss")
return time2text(world.timeofday, format)
/proc/gameTimestamp(format = "hh:mm:ss") // Get the game time in text
return time2text(world.time - timezoneOffset + 432000, format)
/* Returns 1 if it is the selected month and day */
/proc/isDay(month, 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(month == MM && day == DD)
return 1
// Uncomment this out when debugging!
//else
//return 1
//returns timestamp in a sql and ISO 8601 friendly format
/proc/SQLtime()
return time2text(world.realtime, "YYYY-MM-DD hh:mm:ss")
+588
View File
@@ -0,0 +1,588 @@
/*
* Holds procs designed to change one type of value, into another.
* Contains:
* hex2num & num2hex
* file2list
* angle2dir
* angle2text
* worldtime2text
* text2dir_extended & dir2text_short
*/
//Returns an integer given a hex input, supports negative values "-ff"
//skips preceding invalid characters
//breaks when hittin invalid characters thereafter
/proc/hex2num(hex)
. = 0
if(istext(hex))
var/negative = 0
var/len = length(hex)
for(var/i=1, i<=len, i++)
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)
negative = 1//-
else
if(num)
break
else
continue
. *= 16
. += num
if(negative)
. *= -1
return .
//Returns the hex value of a decimal number
//len == length of returned string
//if len < 0 then the returned string will be as long as it needs to be to contain the data
//Only supports positive numbers
//if an invalid number is provided, it assumes num==0
//Note, unlike previous versions, this one works from low to high <-- that way
/proc/num2hex(num, len=2)
if(!isnum(num))
num = 0
num = round(abs(num))
. = ""
var/i=0
while(1)
if(len<=0)
if(!num)
break
else
if(i>=len)
break
var/remainder = num/16
num = round(remainder)
remainder = (remainder - num) * 16
switch(remainder)
if(9,8,7,6,5,4,3,2,1)
. = "[remainder]" + .
if(10,11,12,13,14,15)
. = ascii2text(remainder+87) + .
else
. = "0" + .
i++
return .
//Splits the text of a file at seperator and returns them in a list.
/proc/file2list(filename, seperator="\n")
return splittext(return_file_text(filename),seperator)
//Turns a direction into text
/proc/dir2text(direction)
switch(direction)
if(1)
return "north"
if(2)
return "south"
if(4)
return "east"
if(8)
return "west"
if(5)
return "northeast"
if(6)
return "southeast"
if(9)
return "northwest"
if(10)
return "southwest"
else
return
//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
else
return
//Converts an angle (degrees) into an ss13 direction
/proc/angle2dir(degree)
degree = SimplifyDegrees(degree)
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(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
else
return null
//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="", list/adds, list/subs)
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"
for(var/verbpath in adds)
. += "[seperator]+[verbpath]"
for(var/verbpath in subs)
. += "[seperator]-[verbpath]"
return .
/proc/ui_style2icon(ui_style)
switch(ui_style)
if("Retro")
return 'icons/mob/screen_retro.dmi'
if("Plasmafire")
return 'icons/mob/screen_plasmafire.dmi'
if("Slimecore")
return 'icons/mob/screen_slimecore.dmi'
if("Operative")
return 'icons/mob/screen_operative.dmi'
else
return 'icons/mob/screen_midnight.dmi'
//colour formats
/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
var/hue=0;var/saturation=0;var/lightness=0;
lightness = (max + min)/2
if(range != 0)
if(lightness < 0.5)
saturation = range/(max+min)
else
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
if(max==red)
hue = dblue - dgreen
else if(max==green)
hue = dred - dblue + (1/3)
else
hue = dgreen - dred + (2/3)
if(hue < 0)
hue++
else if(hue > 1)
hue--
return list(hue, saturation, lightness)
/proc/hsl2rgb(hue, saturation, lightness)
var/red;var/green;var/blue;
if(saturation == 0)
red = lightness * 255
green = red
blue = red
else
var/a;var/b;
if(lightness < 0.5)
b = lightness*(1+saturation)
else
b = (lightness+saturation) - (saturation*lightness)
a = 2*lightness - b
red = round(255 * hue2rgb(a, b, hue+(1/3)))
green = round(255 * hue2rgb(a, b, hue))
blue = round(255 * hue2rgb(a, b, hue-(1/3)))
return list(red, green, blue)
/proc/hue2rgb(a, b, hue)
if(hue < 0)
hue++
else if(hue > 1)
hue--
if(6*hue < 1)
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
// 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/year = 1970 //Unix Epoc begins 1970-01-01
var/dayInSeconds = 86400 //60secs*60mins*24hours
var/daysInYear = 365 //Non Leap Year
var/daysInLYear = daysInYear + 1//Leap year
var/days = round(timestamp / dayInSeconds) //Days passed since UNIX Epoc
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)]"
/*
var/list/test_times = list("December" = 1323522004, "August" = 1123522004, "January" = 1011522004,
"Jan Leap" = 946684800, "Jan Normal" = 978307200, "New Years Eve" = 1009670400,
"New Years" = 1009836000, "New Years 2" = 1041372000, "New Years 3" = 1104530400,
"July Month End" = 744161003, "July Month End 12" = 1343777003, "End July" = 1091311200)
for(var/t in test_times)
world.log << "TEST: [t] is [unix2date(test_times[t])]"
*/
/proc/isLeap(y)
return ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
// A copy of text2dir, extended to accept one and two letter
// directions, and to clearly return 0 otherwise.
/proc/text2dir_extended(direction)
switch(uppertext(direction))
if("NORTH", "N")
return 1
if("SOUTH", "S")
return 2
if("EAST", "E")
return 4
if("WEST", "W")
return 8
if("NORTHEAST", "NE")
return 5
if("NORTHWEST", "NW")
return 9
if("SOUTHEAST", "SE")
return 6
if("SOUTHWEST", "SW")
return 10
else
return 0
// A copy of dir2text, which returns the short one or two letter
// directions used in tube icon states.
/proc/dir2text_short(direction)
switch(direction)
if(1)
return "N"
if(2)
return "S"
if(4)
return "E"
if(8)
return "W"
if(5)
return "NE"
if(6)
return "SE"
if(9)
return "NW"
if(10)
return "SW"
else
return
//Turns a Body_parts_covered bitfield into a list of organ/limb names.
//(I challenge you to find a use for this)
/proc/body_parts_covered2organ_names(bpc)
var/list/covered_parts = list()
if(!bpc)
return 0
if(bpc & FULL_BODY)
covered_parts |= list("l_arm","r_arm","head","chest","l_leg","r_leg")
else
if(bpc & HEAD)
covered_parts |= list("head")
if(bpc & CHEST)
covered_parts |= list("chest")
if(bpc & GROIN)
covered_parts |= list("chest")
if(bpc & ARMS)
covered_parts |= list("l_arm","r_arm")
else
if(bpc & ARM_LEFT)
covered_parts |= list("l_arm")
if(bpc & ARM_RIGHT)
covered_parts |= list("r_arm")
if(bpc & HANDS)
covered_parts |= list("l_arm","r_arm")
else
if(bpc & HAND_LEFT)
covered_parts |= list("l_arm")
if(bpc & HAND_RIGHT)
covered_parts |= list("r_arm")
if(bpc & LEGS)
covered_parts |= list("l_leg","r_leg")
else
if(bpc & LEG_LEFT)
covered_parts |= list("l_leg")
if(bpc & LEG_RIGHT)
covered_parts |= list("r_leg")
if(bpc & FEET)
covered_parts |= list("l_leg","r_leg")
else
if(bpc & FOOT_LEFT)
covered_parts |= list("l_leg")
if(bpc & FOOT_RIGHT)
covered_parts |= list("r_leg")
return covered_parts
//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))
/proc/heat2colour_r(temp)
temp /= 100
if(temp <= 66)
. = 255
else
. = max(0, min(255, 329.698727446 * (temp - 60) ** -0.1332047592))
/proc/heat2colour_g(temp)
temp /= 100
if(temp <= 66)
. = max(0, min(255, 99.4708025861 * log(temp) - 161.1195681661))
else
. = max(0, min(255, 288.1221685293 * ((temp - 60) ** -0.075148492)))
/proc/heat2colour_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))
/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"
//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
//assumes format #RRGGBB #rrggbb
/proc/color_hex2num(A)
if(!A)
return 0
var/R = hex2num(copytext(A,2,4))
var/G = hex2num(copytext(A,4,6))
var/B = hex2num(copytext(A,6,0))
return R+G+B
//Converts a positive interger to its roman numeral equivilent. Ignores any decimals.
//Numbers over 3999 will display with extra "M"s (don't tell the Romans) and can get comically long, so be careful.
/proc/num2roman(A)
var/list/values = list("M" = 1000, "CM" = 900, "D" = 500, "CD" = 400, "C" = 100, "XC" = 90, "L" = 50, "XL" = 40, "X" = 10, "IX" = 9, "V" = 5, "IV" = 4, "I" = 1)
if(!A || !isnum(A))
return 0
while(A >= 1)
for(var/i in values)
if(A >= values[i])
. += i
A -= values[i]
break
File diff suppressed because it is too large Load Diff