All the EOLs are now LF.

Fuck you too 0D :^)
This commit is contained in:
PJB3005
2016-01-16 23:28:07 +01:00
parent f2ed22a049
commit 1a2f9ab059
898 changed files with 222641 additions and 222641 deletions

View File

@@ -1,109 +1,109 @@
//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)
error("File not found ([filename])")
return
var/text = file2text(filename)
if(!text)
error("File empty ([filename])")
return
return text
/proc/get_maps(root="maps/voting/")
var/list/maps = list()
var/recursion_limit = 20 //lots of maps waiting to be played, feels like TF2
//Get our potential maps
//testing("starting in [root]")
for(var/potential in flist(root))
if(copytext(potential,-1,0 != "/")) continue // Not a directory, ignore it.
//testing("Inside [root + potential]")
if(!recursion_limit) break
//our current working directory
var/path = root + potential
//The DMB that has the map we want.
var/binary
//Looking for a binary
var/min = -1
var/max = -1
var/skipping = 0
for(var/binaries in flist(path))
//testing("Checking file [binaries]")
if(copytext(binaries,-15,0 == "playercount.txt"))
var/list/lines = file2list(path+binaries)
for(var/line in lines)
if(findtext(line,"max")) max = text2num(copytext(line,5,0))
else if(findtext(line,"min")) min = text2num(copytext(line,5,0))
else warning("Our file had excessive lines, skipping.")
if(!isnull(min) && !isnull(max))
if((min != -1) && clients.len < min)
skipping = 1
else if((max != -1) && clients.len > max)
skipping = 2
if(copytext(binaries,-4,0) == ".dmb")
if(binary)
warning("Extra DMB [binary] in map folder, skipping.")
continue
binary = binaries
continue
if(skipping)
message_admins("Skipping map [binary] due to [skipping == 1 ? "not enough players." : "too many players."]")
warning("Skipping map [binary] due to [skipping == 1 ? "not enough players." : "too many players."]")
binary = null
continue
if(!binary)
warning("Map folder [path] does not contain a valid byond binary, skipping.")
else
maps[potential] = path + binary
binary = null
recursion_limit--
return maps
//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", ".csv"))
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) )
//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)
error("File not found ([filename])")
return
var/text = file2text(filename)
if(!text)
error("File empty ([filename])")
return
return text
/proc/get_maps(root="maps/voting/")
var/list/maps = list()
var/recursion_limit = 20 //lots of maps waiting to be played, feels like TF2
//Get our potential maps
//testing("starting in [root]")
for(var/potential in flist(root))
if(copytext(potential,-1,0 != "/")) continue // Not a directory, ignore it.
//testing("Inside [root + potential]")
if(!recursion_limit) break
//our current working directory
var/path = root + potential
//The DMB that has the map we want.
var/binary
//Looking for a binary
var/min = -1
var/max = -1
var/skipping = 0
for(var/binaries in flist(path))
//testing("Checking file [binaries]")
if(copytext(binaries,-15,0 == "playercount.txt"))
var/list/lines = file2list(path+binaries)
for(var/line in lines)
if(findtext(line,"max")) max = text2num(copytext(line,5,0))
else if(findtext(line,"min")) min = text2num(copytext(line,5,0))
else warning("Our file had excessive lines, skipping.")
if(!isnull(min) && !isnull(max))
if((min != -1) && clients.len < min)
skipping = 1
else if((max != -1) && clients.len > max)
skipping = 2
if(copytext(binaries,-4,0) == ".dmb")
if(binary)
warning("Extra DMB [binary] in map folder, skipping.")
continue
binary = binaries
continue
if(skipping)
message_admins("Skipping map [binary] due to [skipping == 1 ? "not enough players." : "too many players."]")
warning("Skipping map [binary] due to [skipping == 1 ? "not enough players." : "too many players."]")
binary = null
continue
if(!binary)
warning("Map folder [path] does not contain a valid byond binary, skipping.")
else
maps[potential] = path + binary
binary = null
recursion_limit--
return maps
//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", ".csv"))
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) )
to_chat(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 canr each sizes of 4MB! */
/client/proc/file_spam_check()
var/time_to_wait = fileaccess_timer - world.time
if(time_to_wait > 0)
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 canr each sizes of 4MB! */
/client/proc/file_spam_check()
var/time_to_wait = fileaccess_timer - world.time
if(time_to_wait > 0)
to_chat(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
return 1
fileaccess_timer = world.time + FTPDELAY
return 0
#undef FTPDELAY

View File

@@ -1,67 +1,67 @@
var/list/clients = list() //list of all clients
var/list/admins = list() //list of all clients whom are admins
var/list/directory = list() //list of all ckeys with associated client
//Since it didn't really belong in any other category, I'm putting this here
//This is for procs to replace all the goddamn 'in world's that are chilling around the code
var/global/list/player_list = list() //List of all mobs **with clients attached**. Excludes /mob/new_player
var/global/list/mob_list = list() //List of all mobs, including clientless
var/global/list/living_mob_list = list() //List of all alive mobs, including clientless. Excludes /mob/new_player
var/global/list/dead_mob_list = list() //List of all dead mobs, including clientless. Excludes /mob/new_player
var/list/observers = new/list()
var/global/list/areas = list()
var/global/list/turfs = list()
var/global/list/chemical_reactions_list //list of all /datum/chemical_reaction datums. Used during chemical reactions
var/global/list/chemical_reagents_list //list of all /datum/reagent datums indexed by reagent id. Used by chemistry stuff
var/global/list/landmarks_list = list() //list of all landmarks created
var/global/list/surgery_steps = list() //list of all surgery steps |BS12
var/global/list/mechas_list = list() //list of all mechs. Used by hostile mobs target tracking.
// Posters
var/global/list/datum/poster/poster_designs = typesof(/datum/poster) - /datum/poster - /datum/poster/goldstar
//Preferences stuff
//Underwear
var/global/list/underwear_m = list("White", "Grey", "Green", "Blue", "Black", "Mankini", "Love-Hearts", "Black2", "Grey2", "Stripey", "Kinky", "None") //Curse whoever made male/female underwear diffrent colours
var/global/list/underwear_f = list("Red", "White", "Yellow", "Blue", "Black", "Thong", "Babydoll", "Baby-Blue", "Green", "Pink", "Kinky", "None")
//Backpacks
var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Alt")
// This is stupid as fuck.
var/list/hit_appends = list("-OOF", "-ACK", "-UGH", "-HRNK", "-HURGH", "-GLORF")
//*-hud user lists
var/global/list/table_recipes = list() //list of all table craft recipes
var/global/list/med_hud_users = list() //list of all entities using a medical HUD.
var/global/list/sec_hud_users = list() //list of all entities using a security HUD.
//////////////////////////
/////Initial Building/////
//////////////////////////
/proc/make_datum_references_lists()
var/list/paths
//Surgery Steps - Initialize all /datum/surgery_step into a list
paths = typesof(/datum/surgery_step)-/datum/surgery_step
for(var/T in paths)
var/datum/surgery_step/S = new T
surgery_steps += S
sort_surgeries()
/* // 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"
var/list/clients = list() //list of all clients
var/list/admins = list() //list of all clients whom are admins
var/list/directory = list() //list of all ckeys with associated client
//Since it didn't really belong in any other category, I'm putting this here
//This is for procs to replace all the goddamn 'in world's that are chilling around the code
var/global/list/player_list = list() //List of all mobs **with clients attached**. Excludes /mob/new_player
var/global/list/mob_list = list() //List of all mobs, including clientless
var/global/list/living_mob_list = list() //List of all alive mobs, including clientless. Excludes /mob/new_player
var/global/list/dead_mob_list = list() //List of all dead mobs, including clientless. Excludes /mob/new_player
var/list/observers = new/list()
var/global/list/areas = list()
var/global/list/turfs = list()
var/global/list/chemical_reactions_list //list of all /datum/chemical_reaction datums. Used during chemical reactions
var/global/list/chemical_reagents_list //list of all /datum/reagent datums indexed by reagent id. Used by chemistry stuff
var/global/list/landmarks_list = list() //list of all landmarks created
var/global/list/surgery_steps = list() //list of all surgery steps |BS12
var/global/list/mechas_list = list() //list of all mechs. Used by hostile mobs target tracking.
// Posters
var/global/list/datum/poster/poster_designs = typesof(/datum/poster) - /datum/poster - /datum/poster/goldstar
//Preferences stuff
//Underwear
var/global/list/underwear_m = list("White", "Grey", "Green", "Blue", "Black", "Mankini", "Love-Hearts", "Black2", "Grey2", "Stripey", "Kinky", "None") //Curse whoever made male/female underwear diffrent colours
var/global/list/underwear_f = list("Red", "White", "Yellow", "Blue", "Black", "Thong", "Babydoll", "Baby-Blue", "Green", "Pink", "Kinky", "None")
//Backpacks
var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Alt")
// This is stupid as fuck.
var/list/hit_appends = list("-OOF", "-ACK", "-UGH", "-HRNK", "-HURGH", "-GLORF")
//*-hud user lists
var/global/list/table_recipes = list() //list of all table craft recipes
var/global/list/med_hud_users = list() //list of all entities using a medical HUD.
var/global/list/sec_hud_users = list() //list of all entities using a security HUD.
//////////////////////////
/////Initial Building/////
//////////////////////////
/proc/make_datum_references_lists()
var/list/paths
//Surgery Steps - Initialize all /datum/surgery_step into a list
paths = typesof(/datum/surgery_step)-/datum/surgery_step
for(var/T in paths)
var/datum/surgery_step/S = new T
surgery_steps += S
sort_surgeries()
/* // 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"
to_chat(world, .)
*/
*/
var/global/list/escape_list = list()

View File

@@ -1,136 +1,136 @@
proc
getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N
var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A.
for(var/I in A.overlays)//For every image in overlays. var/image/I will not work, don't try it.
if(I:layer>A.layer) continue//If layer is greater than what we need, skip it.
var/icon/image_overlay = new(I:icon,I:icon_state)//Blend only works with icon objects.
//Also, icons cannot directly set icon_state. Slower than changing variables but whatever.
alpha_mask.Blend(image_overlay,ICON_OR)//OR so they are lumped together in a nice overlay.
return alpha_mask//And now return the mask.
/mob/proc/AddCamoOverlay(atom/A)//A is the atom which we are using as the overlay.
var/icon/opacity_icon = new(A.icon, A.icon_state)//Don't really care for overlays/underlays.
//Now we need to culculate overlays+underlays and add them together to form an image for a mask.
//var/icon/alpha_mask = getFlatIcon(src)//Accurate but SLOW. Not designed for running each tick. Could have other uses I guess.
var/icon/alpha_mask = getIconMask(src)//Which is why I created that proc. Also a little slow since it's blending a bunch of icons together but good enough.
opacity_icon.AddAlphaMask(alpha_mask)//Likely the main source of lag for this proc. Probably not designed to run each tick.
opacity_icon.ChangeOpacity(0.4)//Front end for MapColors so it's fast. 0.5 means half opacity and looks the best in my opinion.
for(var/i=0,i<5,i++)//And now we add it as overlays. It's faster than creating an icon and then merging it.
var/image/I = image("icon" = opacity_icon, "icon_state" = A.icon_state, "layer" = layer+0.8)//So it's above other stuff but below weapons and the like.
switch(i)//Now to determine offset so the result is somewhat blurred.
if(1) I.pixel_x--
if(2) I.pixel_x++
if(3) I.pixel_y--
if(4) I.pixel_y++
overlays += I//And finally add the overlay.
/proc/getHologramIcon(icon/A, safety=1)//If safety is on, a new icon is not created.
var/icon/flat_icon = safety ? A : new(A)//Has to be a new icon to not constantly change the same icon.
flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish.
flat_icon.ChangeOpacity(0.5)//Make it half transparent.
var/icon/alpha_mask = new('icons/effects/effects.dmi', "scanline")//Scanline effect.
flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect.
return flat_icon
/proc/getStaticIcon(icon/A, safety=1)
var/icon/flat_icon = safety ? A : new(A)
flat_icon.Blend(rgb(255, 255, 255))
flat_icon.BecomeAlphaMask()
var/icon/static_icon = new/icon('icons/effects/effects.dmi', "static_base")
static_icon.AddAlphaMask(flat_icon)
return static_icon
/proc/getBlankIcon(icon/A, safety=1)
var/icon/flat_icon = safety ? A : new(A)
flat_icon.Blend(rgb(255, 255, 255))
flat_icon.BecomeAlphaMask()
var/icon/blank_icon = new/icon('icons/effects/effects.dmi', "blank_base")
blank_icon.AddAlphaMask(flat_icon)
return blank_icon
/proc/getLetterImage(atom/A, letter = "", uppercase = 0)
if(!A)
return
var/icon/atom_icon = new(A.icon, A.icon_state)
if(!letter)
letter = copytext(A.name, 1, 2)
if(uppercase == 1)
letter = uppertext(letter)
else if(uppercase == -1)
letter = lowertext(letter)
var/image/text_image = new(loc = A)
text_image.maptext = "<font size = 4><b>[letter]</b></font>"
text_image.color = AverageColor(atom_icon)
text_image.pixel_x = 6
text_image.pixel_y = 5
del(atom_icon)
return text_image
//For photo camera.
/proc/build_composite_icon(atom/A)
var/icon/composite = icon(A.icon, A.icon_state, A.dir, 1)
for(var/O in A.overlays)
var/image/I = O
var/icon/C = icon(I.icon, I.icon_state, I.dir, 1)
C.Blend(I.color, ICON_MULTIPLY)
composite.Blend(C, ICON_OVERLAY)
return composite
proc/adjust_brightness(var/color, var/value)
if (!color) return "#FFFFFF"
if (!value) return color
var/list/RGB = ReadRGB(color)
RGB[1] = Clamp(RGB[1]+value,0,255)
RGB[2] = Clamp(RGB[2]+value,0,255)
RGB[3] = Clamp(RGB[3]+value,0,255)
return rgb(RGB[1],RGB[2],RGB[3])
/proc/ListColors(var/icon/I, var/ignoreGreyscale = 0)
var/list/colors = list()
for(var/x_pixel = 1 to I.Width())
for(var/y_pixel = 1 to I.Height())
var/this_color = I.GetPixel(x_pixel, y_pixel)
if(this_color)
if (ignoreGreyscale && ReadHSV(RGBtoHSV(this_color))[2] == 0) //If saturation is 0, must be greyscale
continue
colors.Add(this_color)
return colors
/proc/AverageColor(var/icon/I, var/accurate = 0, var/ignoreGreyscale = 0)
//Accurate: Use more accurate color averaging, usually has better results and prevents muddied or overly dark colors. Mad thanks to wwjnc.
//ignoreGreyscale: Excempts greyscale colors from the color list, useful for filtering outlines or plate overlays.
var/list/colors = ListColors(I, ignoreGreyscale)
if(!colors.len)
return null
var/list/colorsum = list(0, 0, 0) //Holds the sum of the RGB values to calculate the average
var/list/RGB = list(0, 0, 0) //Temp list for each color
var/total = colors.len
var/final_average
if (accurate) //keeping it legible
for(var/i = 1 to total)
RGB = ReadRGB(colors[i])
colorsum[1] += RGB[1]*RGB[1]
colorsum[2] += RGB[2]*RGB[2]
colorsum[3] += RGB[3]*RGB[3]
final_average = rgb(sqrt(colorsum[1]/total), sqrt(colorsum[2]/total), sqrt(colorsum[3]/total))
else
for(var/i = 1 to total)
RGB = ReadRGB(colors[i])
colorsum[1] += RGB[1]
colorsum[2] += RGB[2]
colorsum[3] += RGB[3]
final_average = rgb(colorsum[1]/total, colorsum[2]/total, colorsum[3]/total)
return final_average
/proc/empty_Y_space(var/icon/I) //Returns the amount of lines containing only transparent pixels in an icon, starting from the bottom
for(var/y_pixel = 1 to I.Height())
for(var/x_pixel = 1 to I.Width())
if (I.GetPixel(x_pixel, y_pixel))
return y_pixel - 1
return null
proc
getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N
var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A.
for(var/I in A.overlays)//For every image in overlays. var/image/I will not work, don't try it.
if(I:layer>A.layer) continue//If layer is greater than what we need, skip it.
var/icon/image_overlay = new(I:icon,I:icon_state)//Blend only works with icon objects.
//Also, icons cannot directly set icon_state. Slower than changing variables but whatever.
alpha_mask.Blend(image_overlay,ICON_OR)//OR so they are lumped together in a nice overlay.
return alpha_mask//And now return the mask.
/mob/proc/AddCamoOverlay(atom/A)//A is the atom which we are using as the overlay.
var/icon/opacity_icon = new(A.icon, A.icon_state)//Don't really care for overlays/underlays.
//Now we need to culculate overlays+underlays and add them together to form an image for a mask.
//var/icon/alpha_mask = getFlatIcon(src)//Accurate but SLOW. Not designed for running each tick. Could have other uses I guess.
var/icon/alpha_mask = getIconMask(src)//Which is why I created that proc. Also a little slow since it's blending a bunch of icons together but good enough.
opacity_icon.AddAlphaMask(alpha_mask)//Likely the main source of lag for this proc. Probably not designed to run each tick.
opacity_icon.ChangeOpacity(0.4)//Front end for MapColors so it's fast. 0.5 means half opacity and looks the best in my opinion.
for(var/i=0,i<5,i++)//And now we add it as overlays. It's faster than creating an icon and then merging it.
var/image/I = image("icon" = opacity_icon, "icon_state" = A.icon_state, "layer" = layer+0.8)//So it's above other stuff but below weapons and the like.
switch(i)//Now to determine offset so the result is somewhat blurred.
if(1) I.pixel_x--
if(2) I.pixel_x++
if(3) I.pixel_y--
if(4) I.pixel_y++
overlays += I//And finally add the overlay.
/proc/getHologramIcon(icon/A, safety=1)//If safety is on, a new icon is not created.
var/icon/flat_icon = safety ? A : new(A)//Has to be a new icon to not constantly change the same icon.
flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish.
flat_icon.ChangeOpacity(0.5)//Make it half transparent.
var/icon/alpha_mask = new('icons/effects/effects.dmi', "scanline")//Scanline effect.
flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect.
return flat_icon
/proc/getStaticIcon(icon/A, safety=1)
var/icon/flat_icon = safety ? A : new(A)
flat_icon.Blend(rgb(255, 255, 255))
flat_icon.BecomeAlphaMask()
var/icon/static_icon = new/icon('icons/effects/effects.dmi', "static_base")
static_icon.AddAlphaMask(flat_icon)
return static_icon
/proc/getBlankIcon(icon/A, safety=1)
var/icon/flat_icon = safety ? A : new(A)
flat_icon.Blend(rgb(255, 255, 255))
flat_icon.BecomeAlphaMask()
var/icon/blank_icon = new/icon('icons/effects/effects.dmi', "blank_base")
blank_icon.AddAlphaMask(flat_icon)
return blank_icon
/proc/getLetterImage(atom/A, letter = "", uppercase = 0)
if(!A)
return
var/icon/atom_icon = new(A.icon, A.icon_state)
if(!letter)
letter = copytext(A.name, 1, 2)
if(uppercase == 1)
letter = uppertext(letter)
else if(uppercase == -1)
letter = lowertext(letter)
var/image/text_image = new(loc = A)
text_image.maptext = "<font size = 4><b>[letter]</b></font>"
text_image.color = AverageColor(atom_icon)
text_image.pixel_x = 6
text_image.pixel_y = 5
del(atom_icon)
return text_image
//For photo camera.
/proc/build_composite_icon(atom/A)
var/icon/composite = icon(A.icon, A.icon_state, A.dir, 1)
for(var/O in A.overlays)
var/image/I = O
var/icon/C = icon(I.icon, I.icon_state, I.dir, 1)
C.Blend(I.color, ICON_MULTIPLY)
composite.Blend(C, ICON_OVERLAY)
return composite
proc/adjust_brightness(var/color, var/value)
if (!color) return "#FFFFFF"
if (!value) return color
var/list/RGB = ReadRGB(color)
RGB[1] = Clamp(RGB[1]+value,0,255)
RGB[2] = Clamp(RGB[2]+value,0,255)
RGB[3] = Clamp(RGB[3]+value,0,255)
return rgb(RGB[1],RGB[2],RGB[3])
/proc/ListColors(var/icon/I, var/ignoreGreyscale = 0)
var/list/colors = list()
for(var/x_pixel = 1 to I.Width())
for(var/y_pixel = 1 to I.Height())
var/this_color = I.GetPixel(x_pixel, y_pixel)
if(this_color)
if (ignoreGreyscale && ReadHSV(RGBtoHSV(this_color))[2] == 0) //If saturation is 0, must be greyscale
continue
colors.Add(this_color)
return colors
/proc/AverageColor(var/icon/I, var/accurate = 0, var/ignoreGreyscale = 0)
//Accurate: Use more accurate color averaging, usually has better results and prevents muddied or overly dark colors. Mad thanks to wwjnc.
//ignoreGreyscale: Excempts greyscale colors from the color list, useful for filtering outlines or plate overlays.
var/list/colors = ListColors(I, ignoreGreyscale)
if(!colors.len)
return null
var/list/colorsum = list(0, 0, 0) //Holds the sum of the RGB values to calculate the average
var/list/RGB = list(0, 0, 0) //Temp list for each color
var/total = colors.len
var/final_average
if (accurate) //keeping it legible
for(var/i = 1 to total)
RGB = ReadRGB(colors[i])
colorsum[1] += RGB[1]*RGB[1]
colorsum[2] += RGB[2]*RGB[2]
colorsum[3] += RGB[3]*RGB[3]
final_average = rgb(sqrt(colorsum[1]/total), sqrt(colorsum[2]/total), sqrt(colorsum[3]/total))
else
for(var/i = 1 to total)
RGB = ReadRGB(colors[i])
colorsum[1] += RGB[1]
colorsum[2] += RGB[2]
colorsum[3] += RGB[3]
final_average = rgb(colorsum[1]/total, colorsum[2]/total, colorsum[3]/total)
return final_average
/proc/empty_Y_space(var/icon/I) //Returns the amount of lines containing only transparent pixels in an icon, starting from the bottom
for(var/y_pixel = 1 to I.Height())
for(var/x_pixel = 1 to I.Width())
if (I.GetPixel(x_pixel, y_pixel))
return y_pixel - 1
return null

View File

@@ -1,339 +1,339 @@
/*
* Holds procs to help with list operations
* Contains groups:
* Misc
* Sorting
*/
/*
* Misc
*/
//Returns a list in plain english as a string
/proc/english_list(var/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))
if(IsInRange(index,1,L.len))
return L[index]
else if(index in L)
return L[index]
return
/proc/islist(list/L)
if(istype(L))
return 1
return 0
//Return either pick(list) or null if list is not of type /list or is empty
/proc/safepick(list/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(var/atom/A, var/list/L)
for(var/type in L)
if(istype(A, type))
return 1
return 0
//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(var/list/first, var/list/second, var/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(var/list/first, var/list/second, var/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/sorted_insert(list/L, thing, comparator)
var/pos = L.len
while(pos > 0 && call(comparator)(thing, L[pos]) > 0)
pos--
L.Insert(pos+1, thing)
// Returns the next item in a list
/proc/next_list_item(var/item, var/list/L)
var/i
i = L.Find(item)
if(i == L.len)
i = 1
else
i++
return L[i]
// Returns the previous item in a list
/proc/previous_list_item(var/item, var/list/L)
var/i
i = L.Find(item)
if(i == 1)
i = L.len
else
i--
if(i < L.len || i > L.len)
warning("[__FILE__]L[__LINE__]: [i] is outside of bounds for list, ([L.len])")
return
return L[i]
/*
* Sorting
*/
/*
//Reverses the order of items in the list
/proc/reverselist(var/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(var/list/L)
if(!L)
return
L = L.Copy()
for(var/i=1, i<=L.len, ++i)
L.Swap(i,rand(1,L.len))
return L
//Return a list with no duplicate entries
/proc/uniquelist(var/list/L)
var/list/K = list()
for(var/item in L)
if(!(item in K))
K += item
return K
//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(var/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(var/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
/proc/get_key_by_index(var/list/L, var/index)
var/i = 1
for(var/key in L)
if(index == i)
return key
i++
return null
/proc/count_by_type(var/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
/*
* Holds procs to help with list operations
* Contains groups:
* Misc
* Sorting
*/
/*
* Misc
*/
//Returns a list in plain english as a string
/proc/english_list(var/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))
if(IsInRange(index,1,L.len))
return L[index]
else if(index in L)
return L[index]
return
/proc/islist(list/L)
if(istype(L))
return 1
return 0
//Return either pick(list) or null if list is not of type /list or is empty
/proc/safepick(list/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(var/atom/A, var/list/L)
for(var/type in L)
if(istype(A, type))
return 1
return 0
//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(var/list/first, var/list/second, var/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(var/list/first, var/list/second, var/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/sorted_insert(list/L, thing, comparator)
var/pos = L.len
while(pos > 0 && call(comparator)(thing, L[pos]) > 0)
pos--
L.Insert(pos+1, thing)
// Returns the next item in a list
/proc/next_list_item(var/item, var/list/L)
var/i
i = L.Find(item)
if(i == L.len)
i = 1
else
i++
return L[i]
// Returns the previous item in a list
/proc/previous_list_item(var/item, var/list/L)
var/i
i = L.Find(item)
if(i == 1)
i = L.len
else
i--
if(i < L.len || i > L.len)
warning("[__FILE__]L[__LINE__]: [i] is outside of bounds for list, ([L.len])")
return
return L[i]
/*
* Sorting
*/
/*
//Reverses the order of items in the list
/proc/reverselist(var/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(var/list/L)
if(!L)
return
L = L.Copy()
for(var/i=1, i<=L.len, ++i)
L.Swap(i,rand(1,L.len))
return L
//Return a list with no duplicate entries
/proc/uniquelist(var/list/L)
var/list/K = list()
for(var/item in L)
if(!(item in K))
K += item
return K
//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(var/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(var/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
/proc/get_key_by_index(var/list/L, var/index)
var/i = 1
for(var/key in L)
if(index == i)
return key
i++
return null
/proc/count_by_type(var/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

View File

@@ -1,137 +1,137 @@
//print an error message to world.log
/proc/error(msg)
world.log << "## ERROR: [msg]"
/*
* print a warning message to world.log
*/
/* see setup.dm
//print an error message to world.log
/proc/error(msg)
world.log << "## ERROR: [msg]"
/*
* print a warning message to world.log
*/
/* see setup.dm
#define WARNING(MSG) to_chat(world, "##WARNING: [MSG] in [__FILE__] at line [__LINE__] src: [src] usr: [usr].")
#define warning(msg) world.log << "## WARNING: [msg]"
#define testing(msg) world.log << "## TESTING: [msg]"
#define log_game(text) diary << html_decode("\[[time_stamp()]]GAME: [text]")
#define log_vote(text) diary << html_decode("\[[time_stamp()]]VOTE: [text]")
#define log_access(text) diary << html_decode("\[[time_stamp()]]ACCESS: [text]")
#define log_say(text) diary << html_decode("\[[time_stamp()]]SAY: [text]")
#define log_ooc(text) diary << html_decode("\[[time_stamp()]]OOC: [text]")
#define log_whisper(text) diary << html_decode("\[[time_stamp()]]WHISPER: [text]")
#define log_cultspeak(text) diary << html_decode("\[[time_stamp()]]CULT: [text]")
#define log_narspeak(text) diary << html_decode("\[[time_stamp()]]NARSIE: [text]")
#define log_emote(text) diary << html_decode("\[[time_stamp()]]EMOTE: [text]")
#define log_attack(text) diaryofmeanpeople << html_decode("\[[time_stamp()]]ATTACK: [text]")
#define log_adminsay(text) diary << html_decode("\[[time_stamp()]]ADMINSAY: [text]")
#define log_adminwarn(text) diary << html_decode("\[[time_stamp()]]ADMINWARN: [text]")
#define log_pda(text) diary << html_decode("\[[time_stamp()]]PDA: [text]")
*/
/proc/log_admin(raw_text)
var/text_to_log = "\[[time_stamp()]]ADMIN: [raw_text]"
admin_log.Add(text_to_log)
if(config.log_admin)
diary << html_decode(text_to_log)
if(config.log_admin_only)
admin_diary << html_decode(text_to_log)
/proc/log_debug(text)
if (config.log_debug)
diary << html_decode("\[[time_stamp()]]DEBUG: [text]")
for(var/client/C in admins)
if(C.prefs.toggles & CHAT_DEBUGLOGS)
#define warning(msg) world.log << "## WARNING: [msg]"
#define testing(msg) world.log << "## TESTING: [msg]"
#define log_game(text) diary << html_decode("\[[time_stamp()]]GAME: [text]")
#define log_vote(text) diary << html_decode("\[[time_stamp()]]VOTE: [text]")
#define log_access(text) diary << html_decode("\[[time_stamp()]]ACCESS: [text]")
#define log_say(text) diary << html_decode("\[[time_stamp()]]SAY: [text]")
#define log_ooc(text) diary << html_decode("\[[time_stamp()]]OOC: [text]")
#define log_whisper(text) diary << html_decode("\[[time_stamp()]]WHISPER: [text]")
#define log_cultspeak(text) diary << html_decode("\[[time_stamp()]]CULT: [text]")
#define log_narspeak(text) diary << html_decode("\[[time_stamp()]]NARSIE: [text]")
#define log_emote(text) diary << html_decode("\[[time_stamp()]]EMOTE: [text]")
#define log_attack(text) diaryofmeanpeople << html_decode("\[[time_stamp()]]ATTACK: [text]")
#define log_adminsay(text) diary << html_decode("\[[time_stamp()]]ADMINSAY: [text]")
#define log_adminwarn(text) diary << html_decode("\[[time_stamp()]]ADMINWARN: [text]")
#define log_pda(text) diary << html_decode("\[[time_stamp()]]PDA: [text]")
*/
/proc/log_admin(raw_text)
var/text_to_log = "\[[time_stamp()]]ADMIN: [raw_text]"
admin_log.Add(text_to_log)
if(config.log_admin)
diary << html_decode(text_to_log)
if(config.log_admin_only)
admin_diary << html_decode(text_to_log)
/proc/log_debug(text)
if (config.log_debug)
diary << html_decode("\[[time_stamp()]]DEBUG: [text]")
for(var/client/C in admins)
if(C.prefs.toggles & CHAT_DEBUGLOGS)
to_chat(C, "DEBUG: [text]")
/proc/log_adminghost(text)
if (config.log_adminghost)
diary << html_decode("\[[time_stamp()]]ADMINGHOST: [text]")
message_admins("\[ADMINGHOST\] [text]")
/proc/log_ghost(text)
if (config.log_adminghost)
diary << html_decode("\[[time_stamp()]]GHOST: [text]")
message_admins("\[GHOST\] [text]")
/**
* Helper proc to log attacks or similar events between two mobs.
*/
/proc/add_attacklogs(var/mob/user, var/mob/target, var/what_done, var/object = null, var/addition = null, var/admin_warn = TRUE)
var/user_txt = (user ? "[user][user.ckey ? " ([user.ckey])" : ""]" : "\<NULL USER\>")
var/target_txt = (target ? "[target][target.ckey ? " ([target.ckey])" : ""]" : "\<NULL TARGET\>")
var/object_txt = (object ? " with \the [object]" : "")
var/intent_txt = (user ? " (INTENT: [uppertext(user.a_intent)])" : "")
var/addition_txt = (addition ? " ([addition])" : "")
if (ismob(user))
user.attack_log += text("\[[time_stamp()]\] <span class='danger'>Has [what_done] [target_txt][object_txt].[intent_txt][addition_txt]</span>")
if (ismob(target))
target.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been [what_done] by [user_txt][object_txt].[intent_txt][addition_txt]</font>")
target.LAssailant = (iscarbon(user) ? user : null)
var/log_msg = "<span class='danger'>[user_txt] [what_done] [target_txt][object_txt][intent_txt].</span>[addition_txt] ([formatJumpTo(user, "JMP")])"
log_attack(log_msg)
if (admin_warn)
msg_admin_attack(log_msg)
/**
* Helper proc to log detailed game events easier.
*
* @param user Subject of the action
* @param what_done Description of the action that user has done (e.g. "toggled the PA to 3")
* @param admin Whether to message the admins about this
* @param tp_link Whether to add a jump link to the position of the action (i.e. user.loc)
* @param tp_link_short Whether to make the jump link display 'JMP' instead of the area and coordinates
* @param span_class What CSS class to use for the message.
*/
/proc/add_gamelogs(var/mob/user, var/what_done, var/admin = 1, var/tp_link = FALSE, var/tp_link_short = TRUE, var/span_class = "notice")
var/user_text = (ismob(user) ? "[user] ([user.ckey])" : "<NULL USER>")
var/link = (tp_link ? " ([formatJumpTo(user, (tp_link_short ? "JMP" : ""))])" : "")
var/msg = "<span class='[span_class]'>[user_text] has [what_done].</span>[link]"
log_game(msg)
if (admin)
message_admins(msg)
/**
* Helper function to log reagent transfers, usually 'bad' ones.
*
* @param user The user that performed the transfer
* @param source The item from which the reagents are transferred.
* @param target The destination of the transfer
* @param amount The amount of units transferred
* @param reagent_names List of reagent names to log
*/
/proc/log_reagents(var/mob/user, var/source, var/target, var/amount, var/list/reagent_names)
if (amount == 0)
return
if (reagent_names && reagent_names.len > 0)
var/reagent_text = "<span class='danger'>[english_list(reagent_names)]</span>"
add_gamelogs(user, "added [amount]u (inc. [reagent_text]) to \a [target] with \the [source]", admin = TRUE, tp_link = TRUE)
else
add_gamelogs(user, "added [amount]u to \a [target] with \the [source]", admin = TRUE, tp_link = FALSE)
/**
* Standardized method for tracking startup times.
*/
/proc/log_startup_progress(var/message)
/proc/log_adminghost(text)
if (config.log_adminghost)
diary << html_decode("\[[time_stamp()]]ADMINGHOST: [text]")
message_admins("\[ADMINGHOST\] [text]")
/proc/log_ghost(text)
if (config.log_adminghost)
diary << html_decode("\[[time_stamp()]]GHOST: [text]")
message_admins("\[GHOST\] [text]")
/**
* Helper proc to log attacks or similar events between two mobs.
*/
/proc/add_attacklogs(var/mob/user, var/mob/target, var/what_done, var/object = null, var/addition = null, var/admin_warn = TRUE)
var/user_txt = (user ? "[user][user.ckey ? " ([user.ckey])" : ""]" : "\<NULL USER\>")
var/target_txt = (target ? "[target][target.ckey ? " ([target.ckey])" : ""]" : "\<NULL TARGET\>")
var/object_txt = (object ? " with \the [object]" : "")
var/intent_txt = (user ? " (INTENT: [uppertext(user.a_intent)])" : "")
var/addition_txt = (addition ? " ([addition])" : "")
if (ismob(user))
user.attack_log += text("\[[time_stamp()]\] <span class='danger'>Has [what_done] [target_txt][object_txt].[intent_txt][addition_txt]</span>")
if (ismob(target))
target.attack_log += text("\[[time_stamp()]\] <font color='orange'>Has been [what_done] by [user_txt][object_txt].[intent_txt][addition_txt]</font>")
target.LAssailant = (iscarbon(user) ? user : null)
var/log_msg = "<span class='danger'>[user_txt] [what_done] [target_txt][object_txt][intent_txt].</span>[addition_txt] ([formatJumpTo(user, "JMP")])"
log_attack(log_msg)
if (admin_warn)
msg_admin_attack(log_msg)
/**
* Helper proc to log detailed game events easier.
*
* @param user Subject of the action
* @param what_done Description of the action that user has done (e.g. "toggled the PA to 3")
* @param admin Whether to message the admins about this
* @param tp_link Whether to add a jump link to the position of the action (i.e. user.loc)
* @param tp_link_short Whether to make the jump link display 'JMP' instead of the area and coordinates
* @param span_class What CSS class to use for the message.
*/
/proc/add_gamelogs(var/mob/user, var/what_done, var/admin = 1, var/tp_link = FALSE, var/tp_link_short = TRUE, var/span_class = "notice")
var/user_text = (ismob(user) ? "[user] ([user.ckey])" : "<NULL USER>")
var/link = (tp_link ? " ([formatJumpTo(user, (tp_link_short ? "JMP" : ""))])" : "")
var/msg = "<span class='[span_class]'>[user_text] has [what_done].</span>[link]"
log_game(msg)
if (admin)
message_admins(msg)
/**
* Helper function to log reagent transfers, usually 'bad' ones.
*
* @param user The user that performed the transfer
* @param source The item from which the reagents are transferred.
* @param target The destination of the transfer
* @param amount The amount of units transferred
* @param reagent_names List of reagent names to log
*/
/proc/log_reagents(var/mob/user, var/source, var/target, var/amount, var/list/reagent_names)
if (amount == 0)
return
if (reagent_names && reagent_names.len > 0)
var/reagent_text = "<span class='danger'>[english_list(reagent_names)]</span>"
add_gamelogs(user, "added [amount]u (inc. [reagent_text]) to \a [target] with \the [source]", admin = TRUE, tp_link = TRUE)
else
add_gamelogs(user, "added [amount]u to \a [target] with \the [source]", admin = TRUE, tp_link = FALSE)
/**
* Standardized method for tracking startup times.
*/
/proc/log_startup_progress(var/message)
to_chat(world, "<span class='danger'>[message]</span>")
world.log << message

View File

@@ -1,240 +1,240 @@
/**
* Credits to Nickr5 for the useful procs I've taken from his library resource.
*/
var/const/E = 2.71828183
var/const/Sqrt2 = 1.41421356
/* //All point fingers and laugh at this joke of a list, I even heard using sqrt() is faster than this list lookup, honk.
// 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/Atan2(x, y)
if (!x && !y)
return 0
var/invcos = arccos(x / sqrt(x * x + y * y))
return y >= 0 ? invcos : -invcos
proc/arctan(x)
var/y=arcsin(x/sqrt(1+x*x))
return y
/proc/Ceiling(x, y = 1)
. = -round(-x / y) * y
//Moved to macros.dm to reduce pure calling overhead, this was being called shitloads, like, most calls of all procs.
/*
/proc/Clamp(const/val, const/min, const/max)
if (val <= min)
return min
if (val >= max)
return max
return val
*/
// cotangent
/proc/Cot(x)
return 1 / Tan(x)
// cosecant
/proc/Csc(x)
return 1 / sin(x)
/proc/Default(a, b)
return a ? a : b
/proc/Floor(x = 0, y = 0)
if(x == 0)
return 0
if(y == 0)
return round(x)
if(x < y)
return 0
var/diff = round(x, y) //finds x to the nearest value of y
if(diff > x)
return x - (y - (diff - x)) //diff minus x is the inverse of what we want to remove, so we subtract from y - the base unit - and subtract the result
else
return diff //this is good enough
// 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 Floor(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)
/**
* Generic lerp function.
*/
/proc/lerp(x, x0, x1, y0 = 0, y1 = 1)
return y0 + (y1 - y0)*(x - x0)/(x1 - x0)
/**
* Lerps x to a value between [a, b]. x must be in the range [0, 1].
* My undying gratitude goes out to wwjnc.
*
* Basically this returns the number corresponding to a certain
* percentage in a range. 0% would be a, 100% would be b, 50% would
* be halfways between a and b, and so on.
*
* Other methods of lerping might not yield the exact value of a or b
* when x = 0 or 1. This one guarantees that.
*
* Examples:
* - mix(0.0, 30, 60) = 30
* - mix(1.0, 30, 60) = 60
* - mix(0.5, 30, 60) = 45
* - mix(0.75, 30, 60) = 52.5
*/
/proc/mix(a, b, x)
return a*(1 - x) + b*x
/**
* Lerps x to a value between [0, 1]. x must be in the range [a, b].
*
* This is the counterpart to the mix() function. It returns the actual
* percentage x is at inside the [a, b] range.
*
* Note that this is theoretically equivalent to calling lerp(x, a, b)
* (y0 and y1 default to 0 and 1) but this one is slightly faster
* because Byond is too dumb to optimize procs with default values. It
* shouldn't matter which one you use (since there are no FP issues)
* but this one is more explicit as to what you're doing.
*
* @todo Find a better name for this. I can't into english.
* http://i.imgur.com/8Pu0x7M.png
*/
/proc/unmix(x, a, b, min = 0, max = 1)
if(a==b) return 1
return Clamp( (b - x)/(b - a), min, max )
/proc/Mean(...)
var/values = 0
var/sum = 0
for(var/val in args)
values++
sum += val
return sum / values
/*
* Returns the nth root of x.
*/
/proc/Root(const/n, const/x)
return x ** (1 / n)
/*
* Secant.
*/
/proc/Sec(const/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(const/x)
return sin(x) / cos(x)
/proc/ToDegrees(const/radians)
// 180 / Pi
return radians * 57.2957795
/proc/ToRadians(const/degrees)
// Pi / 180
return degrees * 0.0174532925
// min is inclusive, max is exclusive
/proc/Wrap(val, min, max)
var/d = max - min
var/t = Floor((val - min) / d)
return val - (t * d)
/*
* A very crude linear approximatiaon of pythagoras theorem.
*/
/proc/cheap_pythag(const/Ax, const/Ay)
var/dx = abs(Ax)
var/dy = abs(Ay)
if (dx >= dy)
return dx + (0.5 * dy) // The longest side add half the shortest side approximates the hypotenuse.
else
return dy + (0.5 * dx)
/*
* Magic constants obtained by using linear regression on right-angled triangles of sides 0<x<1, 0<y<1
* They should approximate pythagoras theorem well enough for our needs.
*/
#define k1 0.934
#define k2 0.427
/proc/cheap_hypotenuse(const/Ax, const/Ay, const/Bx, const/By)
var/dx = abs(Ax - Bx) // Sides of right-angled triangle.
var/dy = abs(Ay - By)
if (dx >= dy)
return (k1*dx) + (k2*dy) // No sqrt or powers :).
else
return (k2*dx) + (k1*dy)
#undef k1
#undef k2
//Checks if something's a power of 2, to check bitflags.
//Thanks to wwjnc for this.
/proc/test_bitflag(var/bitflag)
return bitflag != 0 && !(bitflag & (bitflag - 1))
/*
* Diminishing returns formula using a triangular number sequence.
* Taken from http://lostsouls.org/grimoire_diminishing_returns
*/
/proc/triangular_seq(input, scale)
if(input < 0)
return -triangular_seq(-input, scale)
var/mult = input/scale
var/trinum = (sqrt(8 * mult + 1) - 1 ) / 2
return trinum * scale
/**
* Credits to Nickr5 for the useful procs I've taken from his library resource.
*/
var/const/E = 2.71828183
var/const/Sqrt2 = 1.41421356
/* //All point fingers and laugh at this joke of a list, I even heard using sqrt() is faster than this list lookup, honk.
// 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/Atan2(x, y)
if (!x && !y)
return 0
var/invcos = arccos(x / sqrt(x * x + y * y))
return y >= 0 ? invcos : -invcos
proc/arctan(x)
var/y=arcsin(x/sqrt(1+x*x))
return y
/proc/Ceiling(x, y = 1)
. = -round(-x / y) * y
//Moved to macros.dm to reduce pure calling overhead, this was being called shitloads, like, most calls of all procs.
/*
/proc/Clamp(const/val, const/min, const/max)
if (val <= min)
return min
if (val >= max)
return max
return val
*/
// cotangent
/proc/Cot(x)
return 1 / Tan(x)
// cosecant
/proc/Csc(x)
return 1 / sin(x)
/proc/Default(a, b)
return a ? a : b
/proc/Floor(x = 0, y = 0)
if(x == 0)
return 0
if(y == 0)
return round(x)
if(x < y)
return 0
var/diff = round(x, y) //finds x to the nearest value of y
if(diff > x)
return x - (y - (diff - x)) //diff minus x is the inverse of what we want to remove, so we subtract from y - the base unit - and subtract the result
else
return diff //this is good enough
// 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 Floor(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)
/**
* Generic lerp function.
*/
/proc/lerp(x, x0, x1, y0 = 0, y1 = 1)
return y0 + (y1 - y0)*(x - x0)/(x1 - x0)
/**
* Lerps x to a value between [a, b]. x must be in the range [0, 1].
* My undying gratitude goes out to wwjnc.
*
* Basically this returns the number corresponding to a certain
* percentage in a range. 0% would be a, 100% would be b, 50% would
* be halfways between a and b, and so on.
*
* Other methods of lerping might not yield the exact value of a or b
* when x = 0 or 1. This one guarantees that.
*
* Examples:
* - mix(0.0, 30, 60) = 30
* - mix(1.0, 30, 60) = 60
* - mix(0.5, 30, 60) = 45
* - mix(0.75, 30, 60) = 52.5
*/
/proc/mix(a, b, x)
return a*(1 - x) + b*x
/**
* Lerps x to a value between [0, 1]. x must be in the range [a, b].
*
* This is the counterpart to the mix() function. It returns the actual
* percentage x is at inside the [a, b] range.
*
* Note that this is theoretically equivalent to calling lerp(x, a, b)
* (y0 and y1 default to 0 and 1) but this one is slightly faster
* because Byond is too dumb to optimize procs with default values. It
* shouldn't matter which one you use (since there are no FP issues)
* but this one is more explicit as to what you're doing.
*
* @todo Find a better name for this. I can't into english.
* http://i.imgur.com/8Pu0x7M.png
*/
/proc/unmix(x, a, b, min = 0, max = 1)
if(a==b) return 1
return Clamp( (b - x)/(b - a), min, max )
/proc/Mean(...)
var/values = 0
var/sum = 0
for(var/val in args)
values++
sum += val
return sum / values
/*
* Returns the nth root of x.
*/
/proc/Root(const/n, const/x)
return x ** (1 / n)
/*
* Secant.
*/
/proc/Sec(const/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(const/x)
return sin(x) / cos(x)
/proc/ToDegrees(const/radians)
// 180 / Pi
return radians * 57.2957795
/proc/ToRadians(const/degrees)
// Pi / 180
return degrees * 0.0174532925
// min is inclusive, max is exclusive
/proc/Wrap(val, min, max)
var/d = max - min
var/t = Floor((val - min) / d)
return val - (t * d)
/*
* A very crude linear approximatiaon of pythagoras theorem.
*/
/proc/cheap_pythag(const/Ax, const/Ay)
var/dx = abs(Ax)
var/dy = abs(Ay)
if (dx >= dy)
return dx + (0.5 * dy) // The longest side add half the shortest side approximates the hypotenuse.
else
return dy + (0.5 * dx)
/*
* Magic constants obtained by using linear regression on right-angled triangles of sides 0<x<1, 0<y<1
* They should approximate pythagoras theorem well enough for our needs.
*/
#define k1 0.934
#define k2 0.427
/proc/cheap_hypotenuse(const/Ax, const/Ay, const/Bx, const/By)
var/dx = abs(Ax - Bx) // Sides of right-angled triangle.
var/dy = abs(Ay - By)
if (dx >= dy)
return (k1*dx) + (k2*dy) // No sqrt or powers :).
else
return (k2*dx) + (k1*dy)
#undef k1
#undef k2
//Checks if something's a power of 2, to check bitflags.
//Thanks to wwjnc for this.
/proc/test_bitflag(var/bitflag)
return bitflag != 0 && !(bitflag & (bitflag - 1))
/*
* Diminishing returns formula using a triangular number sequence.
* Taken from http://lostsouls.org/grimoire_diminishing_returns
*/
/proc/triangular_seq(input, scale)
if(input < 0)
return -triangular_seq(-input, scale)
var/mult = input/scale
var/trinum = (sqrt(8 * mult + 1) - 1 ) / 2
return trinum * scale

View File

@@ -1,169 +1,169 @@
proc/random_hair_style(gender, species = "Human")
var/h_style = "Bald"
var/list/valid_hairstyles = list()
for(var/hairstyle in hair_styles_list)
var/datum/sprite_accessory/S = hair_styles_list[hairstyle]
if(gender == MALE && S.gender == FEMALE)
continue
if(gender == FEMALE && S.gender == MALE)
continue
if( !(species in S.species_allowed))
continue
valid_hairstyles[hairstyle] = hair_styles_list[hairstyle]
if(valid_hairstyles.len)
h_style = pick(valid_hairstyles)
return h_style
/proc/GetOppositeDir(var/dir)
switch(dir)
if(NORTH) return SOUTH
if(SOUTH) return NORTH
if(EAST) return WEST
if(WEST) return EAST
if(SOUTHWEST) return NORTHEAST
if(NORTHWEST) return SOUTHEAST
if(NORTHEAST) return SOUTHWEST
if(SOUTHEAST) return NORTHWEST
return 0
proc/random_facial_hair_style(gender, species = "Human")
var/f_style = "Shaved"
var/list/valid_facialhairstyles = list()
for(var/facialhairstyle in facial_hair_styles_list)
var/datum/sprite_accessory/S = facial_hair_styles_list[facialhairstyle]
if(gender == MALE && S.gender == FEMALE)
continue
if(gender == FEMALE && S.gender == MALE)
continue
if( !(species in S.species_allowed))
continue
valid_facialhairstyles[facialhairstyle] = facial_hair_styles_list[facialhairstyle]
if(valid_facialhairstyles.len)
f_style = pick(valid_facialhairstyles)
return f_style
proc/random_name(gender, speciesName = "Human")
var/datum/species/S = all_species[speciesName]
if(S)
return S.makeName(gender)
else
var/datum/species/human/H = new
return H.makeName(gender)
proc/random_skin_tone()
switch(pick(60;"caucasian", 15;"afroamerican", 10;"african", 10;"latino", 5;"albino"))
if("caucasian") . = -10
if("afroamerican") . = -115
if("african") . = -165
if("latino") . = -55
if("albino") . = 34
else . = rand(-185,34)
return min(max( .+rand(-25, 25), -185),34)
proc/skintone2racedescription(tone)
switch (tone)
if(30 to INFINITY) return "albino"
if(20 to 30) return "pale"
if(5 to 15) return "light skinned"
if(-10 to 5) return "white"
if(-25 to -10) return "tan"
if(-45 to -25) return "darker skinned"
if(-65 to -45) return "brown"
if(-INFINITY to -65) return "black"
else return "unknown"
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/RoundHealth(health)
switch(health)
if(100 to INFINITY)
return "health100"
if(70 to 100)
return "health80"
if(50 to 70)
return "health60"
if(30 to 50)
return "health40"
if(18 to 30)
return "health25"
if(5 to 18)
return "health10"
if(1 to 5)
return "health1"
if(-99 to 0)
return "health0"
else
return "health-100"
return "0"
/*
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, var/admin=1, var/object=null, var/addition=null)
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]</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]</font>")
if(!iscarbon(user))
target.LAssailant = null
else
target.LAssailant = user
if(admin)
log_attack("<font color='red'>[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]</font>")
proc/add_ghostlogs(var/mob/user, var/obj/target, var/what_done, var/admin=1, var/addition=null)
var/target_text = "NON-EXISTENT TARGET"
var/subject_text = "NON-EXISTENT SUBJECT"
if(target)
target_text=target.name
if(ismob(target))
var/mob/M=target
if(M.ckey)
target_text += "([M.ckey])"
if(user)
subject_text=user.name
if(ismob(user))
var/mob/M=user
if(M.ckey)
subject_text += "([M.ckey])"
if(user && ismob(user))
user.attack_log += "\[[time_stamp()]\] GHOST: <font color='red'>Has [what_done] [target_text] [addition]</font>"
if(target && ismob(target))
var/mob/M=target
M.attack_log += "\[[time_stamp()]\] GHOST: <font color='orange'>Has been [what_done] by [subject_text] [addition]</font>"
if(admin)
//message_admins("GHOST: [subject_text] [what_done] [target_text] [addition]")
if(isAdminGhost(user))
log_adminghost("[subject_text] [what_done] [target_text] [addition]")
else
log_ghost("[subject_text] [what_done] [target_text] [addition]")
/mob/proc/isVentCrawling()
proc/random_hair_style(gender, species = "Human")
var/h_style = "Bald"
var/list/valid_hairstyles = list()
for(var/hairstyle in hair_styles_list)
var/datum/sprite_accessory/S = hair_styles_list[hairstyle]
if(gender == MALE && S.gender == FEMALE)
continue
if(gender == FEMALE && S.gender == MALE)
continue
if( !(species in S.species_allowed))
continue
valid_hairstyles[hairstyle] = hair_styles_list[hairstyle]
if(valid_hairstyles.len)
h_style = pick(valid_hairstyles)
return h_style
/proc/GetOppositeDir(var/dir)
switch(dir)
if(NORTH) return SOUTH
if(SOUTH) return NORTH
if(EAST) return WEST
if(WEST) return EAST
if(SOUTHWEST) return NORTHEAST
if(NORTHWEST) return SOUTHEAST
if(NORTHEAST) return SOUTHWEST
if(SOUTHEAST) return NORTHWEST
return 0
proc/random_facial_hair_style(gender, species = "Human")
var/f_style = "Shaved"
var/list/valid_facialhairstyles = list()
for(var/facialhairstyle in facial_hair_styles_list)
var/datum/sprite_accessory/S = facial_hair_styles_list[facialhairstyle]
if(gender == MALE && S.gender == FEMALE)
continue
if(gender == FEMALE && S.gender == MALE)
continue
if( !(species in S.species_allowed))
continue
valid_facialhairstyles[facialhairstyle] = facial_hair_styles_list[facialhairstyle]
if(valid_facialhairstyles.len)
f_style = pick(valid_facialhairstyles)
return f_style
proc/random_name(gender, speciesName = "Human")
var/datum/species/S = all_species[speciesName]
if(S)
return S.makeName(gender)
else
var/datum/species/human/H = new
return H.makeName(gender)
proc/random_skin_tone()
switch(pick(60;"caucasian", 15;"afroamerican", 10;"african", 10;"latino", 5;"albino"))
if("caucasian") . = -10
if("afroamerican") . = -115
if("african") . = -165
if("latino") . = -55
if("albino") . = 34
else . = rand(-185,34)
return min(max( .+rand(-25, 25), -185),34)
proc/skintone2racedescription(tone)
switch (tone)
if(30 to INFINITY) return "albino"
if(20 to 30) return "pale"
if(5 to 15) return "light skinned"
if(-10 to 5) return "white"
if(-25 to -10) return "tan"
if(-45 to -25) return "darker skinned"
if(-65 to -45) return "brown"
if(-INFINITY to -65) return "black"
else return "unknown"
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/RoundHealth(health)
switch(health)
if(100 to INFINITY)
return "health100"
if(70 to 100)
return "health80"
if(50 to 70)
return "health60"
if(30 to 50)
return "health40"
if(18 to 30)
return "health25"
if(5 to 18)
return "health10"
if(1 to 5)
return "health1"
if(-99 to 0)
return "health0"
else
return "health-100"
return "0"
/*
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, var/admin=1, var/object=null, var/addition=null)
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]</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]</font>")
if(!iscarbon(user))
target.LAssailant = null
else
target.LAssailant = user
if(admin)
log_attack("<font color='red'>[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]</font>")
proc/add_ghostlogs(var/mob/user, var/obj/target, var/what_done, var/admin=1, var/addition=null)
var/target_text = "NON-EXISTENT TARGET"
var/subject_text = "NON-EXISTENT SUBJECT"
if(target)
target_text=target.name
if(ismob(target))
var/mob/M=target
if(M.ckey)
target_text += "([M.ckey])"
if(user)
subject_text=user.name
if(ismob(user))
var/mob/M=user
if(M.ckey)
subject_text += "([M.ckey])"
if(user && ismob(user))
user.attack_log += "\[[time_stamp()]\] GHOST: <font color='red'>Has [what_done] [target_text] [addition]</font>"
if(target && ismob(target))
var/mob/M=target
M.attack_log += "\[[time_stamp()]\] GHOST: <font color='orange'>Has been [what_done] by [subject_text] [addition]</font>"
if(admin)
//message_admins("GHOST: [subject_text] [what_done] [target_text] [addition]")
if(isAdminGhost(user))
log_adminghost("[subject_text] [what_done] [target_text] [addition]")
else
log_ghost("[subject_text] [what_done] [target_text] [addition]")
/mob/proc/isVentCrawling()
return (istype(loc, /obj/machinery/atmospherics)) // Crude but no other situation would put them inside of this

View File

@@ -1,327 +1,327 @@
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")] of [religion_name()]"}
return name
var/command_name = null
/proc/command_name()
if (command_name)
return command_name
var/name = ""
if (prob(10))
name += pick("Super", "Ultra")
name += " "
// Prefix
if (name)
name += pick("", "Central", "System", "Home", "Galactic")
else
name += pick("Central", "System", "Home", "Galactic")
if (name)
name += " "
// Suffix
name += pick("Federation", "Command", "Alliance", "Unity", "Empire", "Confederation", "Protectorate", "Commonwealth", "Imperium", "Republic")
name += " "
command_name = name
return name
/proc/change_command_name(var/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
var/random = rand(1,5)
var/name = ""
//Rare: Pre-Prefix
if (prob(10))
name = pick("Imperium", "Heretical", "Cuban", "Psychic", "Elegant", "Common", "Uncommon", "Rare", "Unique", "Houseruled", "Religious", "Atheist", "Traditional", "Houseruled", "Mad", "Super", "Ultra", "Secret", "Top Secret", "Deep", "Death", "Zybourne", "Central", "Main", "Government", "Uoi", "Fat", "Automated", "Experimental", "Augmented")
station_name = name + " "
// Prefix
switch(Holiday)
//get normal name
if(null,"",0)
name = pick("", "Stanford", "Dorf", "Alium", "Prefix", "Clowning", "Aegis", "Ishimura", "Scaredy", "Death-World", "Mime", "Honk", "Rogue", "MacRagge", "Ultrameens", "Safety", "Paranoia", "Explosive", "Neckbear", "Donk", "Muppet", "North", "West", "East", "South", "Slant-ways", "Widdershins", "Rimward", "Expensive", "Procreatory", "Imperial", "Unidentified", "Immoral", "Carp", "Ork", "Pete", "Control", "Nettle", "Aspie", "Class", "Crab", "Fist","Corrogated","Skeleton","Race", "Fatguy", "Gentleman", "Capitalist", "Communist", "Bear", "Beard", "Derp", "Space", "Spess", "Star", "Moon", "System", "Mining", "Neckbeard", "Research", "Supply", "Military", "Orbital", "Battle", "Science", "Asteroid", "Home", "Production", "Transport", "Delivery", "Extraplanetary", "Orbital", "Correctional", "Robot", "Hats", "Pizza")
if(name)
station_name += name + " "
//For special days like christmas, easter, new-years etc ~Carn
if("Friday the 13th")
name = pick("Mike","Friday","Evil","Myers","Murder","Deathly","Stabby")
station_name += name + " "
random = 13
else
//get the first word of the Holiday and use that
var/i = findtext(Holiday," ",1,0)
name = copytext(Holiday,1,i)
station_name += name + " "
// Suffix
name = pick("Station", "Fortress", "Frontier", "Suffix", "Death-trap", "Space-hulk", "Lab", "Hazard","Spess Junk", "Fishery", "No-Moon", "Tomb", "Crypt", "Hut", "Monkey", "Bomb", "Trade Post", "Fortress", "Village", "Town", "City", "Edition", "Hive", "Complex", "Base", "Facility", "Depot", "Outpost", "Installation", "Drydock", "Observatory", "Array", "Relay", "Monitor", "Platform", "Construct", "Hangar", "Prison", "Center", "Port", "Waystation", "Factory", "Waypoint", "Stopover", "Hub", "HQ", "Office", "Object", "Fortification", "Colony", "Planet-Cracker", "Roost", "Fat Camp")
station_name += name + " "
// ID Number
switch(random)
if(1)
station_name += "[rand(1, 99)]"
if(2)
station_name += pick("Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda", "Mu", "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi", "Omega")
if(3)
station_name += pick("II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX")
if(4)
station_name += pick("Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-ray", "Yankee", "Zulu")
if(5)
station_name += pick("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen")
if(13)
station_name += pick("13","XIII","Thirteen")
if (config && config.server_name)
world.name = "[config.server_name]: [name]"
else
world.name = station_name
return station_name
/proc/world_name(var/name)
station_name = name
if (config && config.server_name)
world.name = "[config.server_name]: [name]"
else
world.name = name
return 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
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
/*
//This proc tests the gen above.
/client/verb/test_code_phrase()
set name = "Generate Code Phrase"
set category = "Debug"
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")] of [religion_name()]"}
return name
var/command_name = null
/proc/command_name()
if (command_name)
return command_name
var/name = ""
if (prob(10))
name += pick("Super", "Ultra")
name += " "
// Prefix
if (name)
name += pick("", "Central", "System", "Home", "Galactic")
else
name += pick("Central", "System", "Home", "Galactic")
if (name)
name += " "
// Suffix
name += pick("Federation", "Command", "Alliance", "Unity", "Empire", "Confederation", "Protectorate", "Commonwealth", "Imperium", "Republic")
name += " "
command_name = name
return name
/proc/change_command_name(var/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
var/random = rand(1,5)
var/name = ""
//Rare: Pre-Prefix
if (prob(10))
name = pick("Imperium", "Heretical", "Cuban", "Psychic", "Elegant", "Common", "Uncommon", "Rare", "Unique", "Houseruled", "Religious", "Atheist", "Traditional", "Houseruled", "Mad", "Super", "Ultra", "Secret", "Top Secret", "Deep", "Death", "Zybourne", "Central", "Main", "Government", "Uoi", "Fat", "Automated", "Experimental", "Augmented")
station_name = name + " "
// Prefix
switch(Holiday)
//get normal name
if(null,"",0)
name = pick("", "Stanford", "Dorf", "Alium", "Prefix", "Clowning", "Aegis", "Ishimura", "Scaredy", "Death-World", "Mime", "Honk", "Rogue", "MacRagge", "Ultrameens", "Safety", "Paranoia", "Explosive", "Neckbear", "Donk", "Muppet", "North", "West", "East", "South", "Slant-ways", "Widdershins", "Rimward", "Expensive", "Procreatory", "Imperial", "Unidentified", "Immoral", "Carp", "Ork", "Pete", "Control", "Nettle", "Aspie", "Class", "Crab", "Fist","Corrogated","Skeleton","Race", "Fatguy", "Gentleman", "Capitalist", "Communist", "Bear", "Beard", "Derp", "Space", "Spess", "Star", "Moon", "System", "Mining", "Neckbeard", "Research", "Supply", "Military", "Orbital", "Battle", "Science", "Asteroid", "Home", "Production", "Transport", "Delivery", "Extraplanetary", "Orbital", "Correctional", "Robot", "Hats", "Pizza")
if(name)
station_name += name + " "
//For special days like christmas, easter, new-years etc ~Carn
if("Friday the 13th")
name = pick("Mike","Friday","Evil","Myers","Murder","Deathly","Stabby")
station_name += name + " "
random = 13
else
//get the first word of the Holiday and use that
var/i = findtext(Holiday," ",1,0)
name = copytext(Holiday,1,i)
station_name += name + " "
// Suffix
name = pick("Station", "Fortress", "Frontier", "Suffix", "Death-trap", "Space-hulk", "Lab", "Hazard","Spess Junk", "Fishery", "No-Moon", "Tomb", "Crypt", "Hut", "Monkey", "Bomb", "Trade Post", "Fortress", "Village", "Town", "City", "Edition", "Hive", "Complex", "Base", "Facility", "Depot", "Outpost", "Installation", "Drydock", "Observatory", "Array", "Relay", "Monitor", "Platform", "Construct", "Hangar", "Prison", "Center", "Port", "Waystation", "Factory", "Waypoint", "Stopover", "Hub", "HQ", "Office", "Object", "Fortification", "Colony", "Planet-Cracker", "Roost", "Fat Camp")
station_name += name + " "
// ID Number
switch(random)
if(1)
station_name += "[rand(1, 99)]"
if(2)
station_name += pick("Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda", "Mu", "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi", "Omega")
if(3)
station_name += pick("II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX")
if(4)
station_name += pick("Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-ray", "Yankee", "Zulu")
if(5)
station_name += pick("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen")
if(13)
station_name += pick("13","XIII","Thirteen")
if (config && config.server_name)
world.name = "[config.server_name]: [name]"
else
world.name = station_name
return station_name
/proc/world_name(var/name)
station_name = name
if (config && config.server_name)
world.name = "[config.server_name]: [name]"
else
world.name = name
return 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
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
/*
//This proc tests the gen above.
/client/verb/test_code_phrase()
set name = "Generate Code Phrase"
set category = "Debug"
to_chat(world, "<span class='warning'>Code Phrase is: </span>[generate_code_phrase()]")
return
This was an earlier attempt at code phrase system, aside from an even earlier attempt (and failure).
This system more or less works as intended--aside from being unfinished--but it's still very predictable.
Particularly, the phrase opening statements are pretty easy to recognize and identify when metagaming.
I think the above-used method solves this issue by using words in a sequence, providing for much greater flexibility.
/N
switch(choice)
if(1)
syndicate_code_phrase += pick("I'm looking for","Have you seen","Maybe you've seen","I'm trying to find","I'm tracking")
syndicate_code_phrase += " "
syndicate_code_phrase += pick(pick(first_names_male,first_names_female))
syndicate_code_phrase += " "
syndicate_code_phrase += pick(last_names)
syndicate_code_phrase += "."
if(2)
syndicate_code_phrase += pick("How do I get to","How do I find","Where is","Where do I find")
syndicate_code_phrase += " "
syndicate_code_phrase += pick("Escape","Engineering","Atmos","the bridge","the brig","Clown Planet","CentCom","the library","the chapel","a bathroom","Med Bay","Tool Storage","the escape shuttle","Robotics","a locker room","the living quarters","the gym","the autolathe","QM","the bar","the theater","the derelict")
syndicate_code_phrase += "?"
if(3)
if(prob(70))
syndicate_code_phrase += pick("Get me","I want","I'd like","Make me")
syndicate_code_phrase += " a "
else
syndicate_code_phrase += pick("One")
syndicate_code_phrase += " "
syndicate_code_phrase += pick("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")
syndicate_code_phrase += "."
if(4)
syndicate_code_phrase += pick("I wish I was","My dad was","His mom was","Where do I find","The hero this station needs is","I'd fuck","I wouldn't trust","Someone caught","HoS caught","Someone found","I'd wrestle","I wanna kill")
syndicate_code_phrase += " [pick("a","the")] "
syndicate_code_phrase += pick("wizard","ninja","xeno","lizard","slime","monkey","syndicate","cyborg","clown","space carp","singularity","singulo","mime")
syndicate_code_phrase += "."
if(5)
syndicate_code_phrase += pick("Do we have","Is there","Where is","Where's","Who's")
syndicate_code_phrase += " "
syndicate_code_phrase += "[pick(get_all_jobs())]"
syndicate_code_phrase += "?"
switch(choice)
if(1)
if(prob(80))
syndicate_code_response += pick("Try looking for them near","I they ran off to","Yes. I saw them near","Nope. I'm heading to","Try searching")
syndicate_code_response += " "
syndicate_code_response += pick("Escape","Engineering","Atmos","the bridge","the brig","Clown Planet","CentCom","the library","the chapel","a bathroom","Med Bay","Tool Storage","the escape shuttle","Robotics","a locker room","the living quarters","the gym","the autolathe","QM","the bar","the theater","the derelict")
syndicate_code_response += "."
else if(prob(60))
syndicate_code_response += pick("No. I'm busy, sorry.","I don't have the time.","Not sure, maybe?","There is no time.")
else
syndicate_code_response += pick("*shrug*","*smile*","*blink*","*sigh*","*laugh*","*nod*","*giggle*")
if(2)
if(prob(80))
syndicate_code_response += pick("Go to","Navigate to","Try","Sure, run to","Try searching","It's near","It's around")
syndicate_code_response += " the "
syndicate_code_response += pick("[pick("south","north","east","west")] maitenance door","nearby maitenance","teleporter","[pick("cold","dead")] space","morgue","vacuum","[pick("south","north","east","west")] hall ","[pick("south","north","east","west")] hallway","[pick("white","black","red","green","blue","pink","purple")] [pick("rabbit","frog","lion","tiger","panther","snake","facehugger")]")
syndicate_code_response += "."
else if(prob(60))
syndicate_code_response += pick("Try asking","Ask","Talk to","Go see","Follow","Hunt down")
syndicate_code_response += " "
if(prob(50))
syndicate_code_response += pick(pick(first_names_male,first_names_female))
syndicate_code_response += " "
syndicate_code_response += pick(last_names)
else
syndicate_code_response += " the "
syndicate_code_response += "[pic(get_all_jobs())]"
syndicate_code_response += "."
else
syndicate_code_response += pick("*shrug*","*smile*","*blink*","*sigh*","*laugh*","*nod*","*giggle*")
if(3)
if(4)
if(5)
return
return
This was an earlier attempt at code phrase system, aside from an even earlier attempt (and failure).
This system more or less works as intended--aside from being unfinished--but it's still very predictable.
Particularly, the phrase opening statements are pretty easy to recognize and identify when metagaming.
I think the above-used method solves this issue by using words in a sequence, providing for much greater flexibility.
/N
switch(choice)
if(1)
syndicate_code_phrase += pick("I'm looking for","Have you seen","Maybe you've seen","I'm trying to find","I'm tracking")
syndicate_code_phrase += " "
syndicate_code_phrase += pick(pick(first_names_male,first_names_female))
syndicate_code_phrase += " "
syndicate_code_phrase += pick(last_names)
syndicate_code_phrase += "."
if(2)
syndicate_code_phrase += pick("How do I get to","How do I find","Where is","Where do I find")
syndicate_code_phrase += " "
syndicate_code_phrase += pick("Escape","Engineering","Atmos","the bridge","the brig","Clown Planet","CentCom","the library","the chapel","a bathroom","Med Bay","Tool Storage","the escape shuttle","Robotics","a locker room","the living quarters","the gym","the autolathe","QM","the bar","the theater","the derelict")
syndicate_code_phrase += "?"
if(3)
if(prob(70))
syndicate_code_phrase += pick("Get me","I want","I'd like","Make me")
syndicate_code_phrase += " a "
else
syndicate_code_phrase += pick("One")
syndicate_code_phrase += " "
syndicate_code_phrase += pick("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")
syndicate_code_phrase += "."
if(4)
syndicate_code_phrase += pick("I wish I was","My dad was","His mom was","Where do I find","The hero this station needs is","I'd fuck","I wouldn't trust","Someone caught","HoS caught","Someone found","I'd wrestle","I wanna kill")
syndicate_code_phrase += " [pick("a","the")] "
syndicate_code_phrase += pick("wizard","ninja","xeno","lizard","slime","monkey","syndicate","cyborg","clown","space carp","singularity","singulo","mime")
syndicate_code_phrase += "."
if(5)
syndicate_code_phrase += pick("Do we have","Is there","Where is","Where's","Who's")
syndicate_code_phrase += " "
syndicate_code_phrase += "[pick(get_all_jobs())]"
syndicate_code_phrase += "?"
switch(choice)
if(1)
if(prob(80))
syndicate_code_response += pick("Try looking for them near","I they ran off to","Yes. I saw them near","Nope. I'm heading to","Try searching")
syndicate_code_response += " "
syndicate_code_response += pick("Escape","Engineering","Atmos","the bridge","the brig","Clown Planet","CentCom","the library","the chapel","a bathroom","Med Bay","Tool Storage","the escape shuttle","Robotics","a locker room","the living quarters","the gym","the autolathe","QM","the bar","the theater","the derelict")
syndicate_code_response += "."
else if(prob(60))
syndicate_code_response += pick("No. I'm busy, sorry.","I don't have the time.","Not sure, maybe?","There is no time.")
else
syndicate_code_response += pick("*shrug*","*smile*","*blink*","*sigh*","*laugh*","*nod*","*giggle*")
if(2)
if(prob(80))
syndicate_code_response += pick("Go to","Navigate to","Try","Sure, run to","Try searching","It's near","It's around")
syndicate_code_response += " the "
syndicate_code_response += pick("[pick("south","north","east","west")] maitenance door","nearby maitenance","teleporter","[pick("cold","dead")] space","morgue","vacuum","[pick("south","north","east","west")] hall ","[pick("south","north","east","west")] hallway","[pick("white","black","red","green","blue","pink","purple")] [pick("rabbit","frog","lion","tiger","panther","snake","facehugger")]")
syndicate_code_response += "."
else if(prob(60))
syndicate_code_response += pick("Try asking","Ask","Talk to","Go see","Follow","Hunt down")
syndicate_code_response += " "
if(prob(50))
syndicate_code_response += pick(pick(first_names_male,first_names_female))
syndicate_code_response += " "
syndicate_code_response += pick(last_names)
else
syndicate_code_response += " the "
syndicate_code_response += "[pic(get_all_jobs())]"
syndicate_code_response += "."
else
syndicate_code_response += pick("*shrug*","*smile*","*blink*","*sigh*","*laugh*","*nod*","*giggle*")
if(3)
if(4)
if(5)
return
*/

View File

@@ -1,46 +1,46 @@
//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 List[1]
//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, default="#000000")
if(!istext(color)) return default
var/len = length(color)
if(len != 7 && len !=4) return default
if(text2ascii(color,1) != 35) return default //35 is the ascii code for "#"
. = "#"
for(var/i=2,i<=len,i++)
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 return default
return .
//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 List[1]
//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, default="#000000")
if(!istext(color)) return default
var/len = length(color)
if(len != 7 && len !=4) return default
if(text2ascii(color,1) != 35) return default //35 is the ascii code for "#"
. = "#"
for(var/i=2,i<=len,i++)
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 return default
return .

View File

@@ -1,480 +1,480 @@
/*
* Holds procs designed to help with filtering text
* Contains groups:
* SQL sanitization
* 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(var/t as text)
//var/sanitized_text = replacetext(t, "'", "\\'")
//sanitized_text = replacetext(sanitized_text, "\"", "\\\"")
var/sqltext = dbcon.Quote(t)
//testing("sanitizeSQL(): BEFORE copytext(): [sqltext]")
sqltext = copytext(sqltext, 2, length(sqltext))//Quote() adds quotes around input, we already do that
//testing("sanitizeSQL(): AFTER copytext(): [sqltext]")
return sqltext
/*
/mob/verb/SanitizeTest(var/t as text)
/*
* Holds procs designed to help with filtering text
* Contains groups:
* SQL sanitization
* 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(var/t as text)
//var/sanitized_text = replacetext(t, "'", "\\'")
//sanitized_text = replacetext(sanitized_text, "\"", "\\\"")
var/sqltext = dbcon.Quote(t)
//testing("sanitizeSQL(): BEFORE copytext(): [sqltext]")
sqltext = copytext(sqltext, 2, length(sqltext))//Quote() adds quotes around input, we already do that
//testing("sanitizeSQL(): AFTER copytext(): [sqltext]")
return sqltext
/*
/mob/verb/SanitizeTest(var/t as text)
to_chat(src, "IN: [t]")
to_chat(src, "OUT: [sanitizeSQL(t)]")
*/
/*
* Text sanitization
*/
//Simply removes < and > and limits the length of the message
/proc/strip_html_simple(var/t,var/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
/proc/strip_html_properly(input = "")
// these store the position of < and > respectively
var/opentag = 0
var/closetag = 0
while (input)
opentag = rfindtext(input, "<")
closetag = findtext(input, ">", opentag + 1)
if (!opentag || !closetag)
break
input = copytext(input, 1, opentag) + copytext(input, closetag + 1)
return input
/proc/rfindtext(Haystack, Needle, Start = 1, End = 0)
var/i = findtext(Haystack, Needle, Start, End)
while (i)
. = i
i = findtext(Haystack, Needle, i + 1, End)
//Removes a few problematic characters
/proc/sanitize_simple(var/t,var/list/repl_chars = list("\n"="#","\t"="#","<22>"="<22>"))
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)
return t
//Runs byond's sanitization proc along-side sanitize_simple
/proc/sanitize(var/t,var/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(var/t,var/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(var/t,var/limit=MAX_MESSAGE_LEN)
return copytext((html_encode(strip_html_simple(t))),1,limit)
/proc/reverse_text(txt)
var/i = length(txt)+1
. = ""
while(--i)
. += copytext(txt,i,i+1)
/*
* returns null if there is any bad text in the string
*/
/proc/reject_bad_text(const/text, var/max_length = 512)
var/text_length = length(text)
if(text_length > max_length)
return // message too long
var/non_whitespace = FALSE
for(var/i = 1 to text_length)
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 <20>
if(0 to 31)
return // more weird stuff
if(32)
continue //whitespace
else
non_whitespace = TRUE
if(non_whitespace)
return text // only accepts the text if it has some non-spaces
// Used to get a sanitized input.
/proc/stripped_input(var/mob/user, var/message = "", var/title = "", var/default = "", var/max_length=MAX_MESSAGE_LEN)
var/name = input(user, message, title, default)
return strip_html_simple(name, max_length)
//Filters out undesirable characters from names
/proc/reject_bad_name(var/t_in, var/allow_numbers=0, var/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","plating")) //prevents these common metagamey names
if(cmptext(t_out,bad_name)) return //(not case sensitive)
return t_out
//checks text for html tags
//if tag is not in whitelist (var/list/paper_tag_whitelist in global.dm)
//relpaces < with &lt;
proc/checkhtml(var/t)
t = sanitize_simple(t, list("&#"="."))
var/p = findtext(t,"<",1)
while (p) //going through all the tags
var/start = p++
var/tag = copytext(t,p, p+1)
if (tag != "/")
while (reject_bad_text(copytext(t, p, p+1), 1))
tag = copytext(t,start, p)
p++
tag = copytext(t,start+1, p)
if (!(tag in paper_tag_whitelist)) //if it's unkown tag, disarming it
t = copytext(t,1,start-1) + "&lt;" + copytext(t,start+1)
p = findtext(t,"<",p)
return t
/*
* 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)
/*
* Text modification
*/
/proc/replacetext(text, find, replacement)
return list2text(text2list(text, find), replacement)
/proc/replacetextEx(text, find, replacement)
return list2text(text2listEx(text, find), replacement)
//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)
return trim_left(trim_right(text))
//Returns a string with the first element of the string capitalized.
/proc/capitalize(var/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(var/text,var/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(length(text) != length(compare))
return 0
for(var/i = 1, i < length(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(var/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 <= length(text), i++)
var/a = copytext(text,i,i+1)
if(a == character)
count++
return count
/**
* Format number with thousands seperators.
* @param number Number to format.
* @param sep seperator to use
*/
/proc/format_num(var/number, var/sep=",")
var/c="" // Current char
var/list/parts = text2list("[number]",".")
var/origtext = "[parts[1]]"
var/len = length(origtext)
var/offset = len % 3
for(var/i=1;i<=len;i++)
c = copytext(origtext,i,i+1)
. += c
if((i%3)==offset && i!=len)
. += sep
if(parts.len==2)
. += ".[parts[2]]"
var/global/list/watt_suffixes = list("W", "KW", "MW", "GW", "TW", "PW", "EW", "ZW", "YW")
/proc/format_watts(var/number)
if(number<0) return "-[format_watts(number)]"
if(number==0) return "0 W"
var/i=1
while (round(number/1000) >= 1)
number/=1000
i++
return "[format_num(number)] [watt_suffixes[i]]"
// Custom algorithm since stackoverflow is full of complete garbage and even the MS algorithm sucks.
// Uses recursion, in places.
// (c)2015 Rob "N3X15" Nelson <nexisentertainment@gmail.com>
// Available under the MIT license.
var/list/number_digits=list(
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
"eleven",
"twelve",
"thirteen",
"fourteen",
"fifteen",
"sixteen",
"seventeen",
"eighteen",
"nineteen",
)
var/list/number_tens=list(
null, // 0 :V
null, // teens, special case
"twenty",
"thirty",
"forty",
"fifty",
"sixty",
"seventy",
"eighty",
"ninety"
)
var/list/number_units=list(
null, // Don't yell units
"thousand",
"million",
"billion"
)
/proc/num2words(var/number, var/zero="zero", var/minus="minus", var/hundred="hundred", var/list/digits=number_digits, var/list/tens=number_tens, var/list/units=number_units, var/recursion=0)
if(!isnum(number))
warning("num2words fed a non-number: [number]")
return list()
number=round(number)
//testing("num2words [recursion] ([number])")
if(number == 0)
return list(zero)
if(number < 0)
return list(minus) + num2words(abs(number), zero, minus, hundred, digits, tens, units, recursion+1)
var/list/out=list()
if(number < 1000)
var/hundreds = round(number/100)
//testing(" ([recursion]) hundreds=[hundreds]")
if(hundreds)
out += num2words(hundreds, zero, minus, hundred, digits, tens, units, recursion+1) + list(hundred)
number %= 100
if(number < 100)
// Teens
if(number <= 19)
out.Add(digits[number])
else
var/tens_place = tens[round(number/10)+1]
//testing(" ([recursion]) tens_place=[round(number/10)+1] = [tens_place]")
if(tens_place!=null)
out.Add(tens_place)
number = number%10
//testing(" ([recursion]) number%10+1 = [number+1] = [digits[number+1]]")
if(number>0)
out.Add(digits[number])
else
var/i=1
while(round(number) > 0)
var/unit_number = number%1000
//testing(" ([recursion]) [number]%1000 = [unit_number] ([i])")
if(unit_number > 0)
if(units[i])
//testing(" ([recursion]) units = [units[i]]")
out = list(units[i]) + out
out = num2words(unit_number, zero, minus, hundred, digits, tens, units, recursion+1) + out
number /= 1000
i++
//testing(" ([recursion]) out=list("+list2text(out,", ")+")")
return out
///mob/verb/test_num2words(var/number as num)
*/
/*
* Text sanitization
*/
//Simply removes < and > and limits the length of the message
/proc/strip_html_simple(var/t,var/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
/proc/strip_html_properly(input = "")
// these store the position of < and > respectively
var/opentag = 0
var/closetag = 0
while (input)
opentag = rfindtext(input, "<")
closetag = findtext(input, ">", opentag + 1)
if (!opentag || !closetag)
break
input = copytext(input, 1, opentag) + copytext(input, closetag + 1)
return input
/proc/rfindtext(Haystack, Needle, Start = 1, End = 0)
var/i = findtext(Haystack, Needle, Start, End)
while (i)
. = i
i = findtext(Haystack, Needle, i + 1, End)
//Removes a few problematic characters
/proc/sanitize_simple(var/t,var/list/repl_chars = list("\n"="#","\t"="#","<22>"="<22>"))
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)
return t
//Runs byond's sanitization proc along-side sanitize_simple
/proc/sanitize(var/t,var/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(var/t,var/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(var/t,var/limit=MAX_MESSAGE_LEN)
return copytext((html_encode(strip_html_simple(t))),1,limit)
/proc/reverse_text(txt)
var/i = length(txt)+1
. = ""
while(--i)
. += copytext(txt,i,i+1)
/*
* returns null if there is any bad text in the string
*/
/proc/reject_bad_text(const/text, var/max_length = 512)
var/text_length = length(text)
if(text_length > max_length)
return // message too long
var/non_whitespace = FALSE
for(var/i = 1 to text_length)
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 <20>
if(0 to 31)
return // more weird stuff
if(32)
continue //whitespace
else
non_whitespace = TRUE
if(non_whitespace)
return text // only accepts the text if it has some non-spaces
// Used to get a sanitized input.
/proc/stripped_input(var/mob/user, var/message = "", var/title = "", var/default = "", var/max_length=MAX_MESSAGE_LEN)
var/name = input(user, message, title, default)
return strip_html_simple(name, max_length)
//Filters out undesirable characters from names
/proc/reject_bad_name(var/t_in, var/allow_numbers=0, var/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","plating")) //prevents these common metagamey names
if(cmptext(t_out,bad_name)) return //(not case sensitive)
return t_out
//checks text for html tags
//if tag is not in whitelist (var/list/paper_tag_whitelist in global.dm)
//relpaces < with &lt;
proc/checkhtml(var/t)
t = sanitize_simple(t, list("&#"="."))
var/p = findtext(t,"<",1)
while (p) //going through all the tags
var/start = p++
var/tag = copytext(t,p, p+1)
if (tag != "/")
while (reject_bad_text(copytext(t, p, p+1), 1))
tag = copytext(t,start, p)
p++
tag = copytext(t,start+1, p)
if (!(tag in paper_tag_whitelist)) //if it's unkown tag, disarming it
t = copytext(t,1,start-1) + "&lt;" + copytext(t,start+1)
p = findtext(t,"<",p)
return t
/*
* 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)
/*
* Text modification
*/
/proc/replacetext(text, find, replacement)
return list2text(text2list(text, find), replacement)
/proc/replacetextEx(text, find, replacement)
return list2text(text2listEx(text, find), replacement)
//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)
return trim_left(trim_right(text))
//Returns a string with the first element of the string capitalized.
/proc/capitalize(var/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(var/text,var/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(length(text) != length(compare))
return 0
for(var/i = 1, i < length(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(var/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 <= length(text), i++)
var/a = copytext(text,i,i+1)
if(a == character)
count++
return count
/**
* Format number with thousands seperators.
* @param number Number to format.
* @param sep seperator to use
*/
/proc/format_num(var/number, var/sep=",")
var/c="" // Current char
var/list/parts = text2list("[number]",".")
var/origtext = "[parts[1]]"
var/len = length(origtext)
var/offset = len % 3
for(var/i=1;i<=len;i++)
c = copytext(origtext,i,i+1)
. += c
if((i%3)==offset && i!=len)
. += sep
if(parts.len==2)
. += ".[parts[2]]"
var/global/list/watt_suffixes = list("W", "KW", "MW", "GW", "TW", "PW", "EW", "ZW", "YW")
/proc/format_watts(var/number)
if(number<0) return "-[format_watts(number)]"
if(number==0) return "0 W"
var/i=1
while (round(number/1000) >= 1)
number/=1000
i++
return "[format_num(number)] [watt_suffixes[i]]"
// Custom algorithm since stackoverflow is full of complete garbage and even the MS algorithm sucks.
// Uses recursion, in places.
// (c)2015 Rob "N3X15" Nelson <nexisentertainment@gmail.com>
// Available under the MIT license.
var/list/number_digits=list(
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
"eleven",
"twelve",
"thirteen",
"fourteen",
"fifteen",
"sixteen",
"seventeen",
"eighteen",
"nineteen",
)
var/list/number_tens=list(
null, // 0 :V
null, // teens, special case
"twenty",
"thirty",
"forty",
"fifty",
"sixty",
"seventy",
"eighty",
"ninety"
)
var/list/number_units=list(
null, // Don't yell units
"thousand",
"million",
"billion"
)
/proc/num2words(var/number, var/zero="zero", var/minus="minus", var/hundred="hundred", var/list/digits=number_digits, var/list/tens=number_tens, var/list/units=number_units, var/recursion=0)
if(!isnum(number))
warning("num2words fed a non-number: [number]")
return list()
number=round(number)
//testing("num2words [recursion] ([number])")
if(number == 0)
return list(zero)
if(number < 0)
return list(minus) + num2words(abs(number), zero, minus, hundred, digits, tens, units, recursion+1)
var/list/out=list()
if(number < 1000)
var/hundreds = round(number/100)
//testing(" ([recursion]) hundreds=[hundreds]")
if(hundreds)
out += num2words(hundreds, zero, minus, hundred, digits, tens, units, recursion+1) + list(hundred)
number %= 100
if(number < 100)
// Teens
if(number <= 19)
out.Add(digits[number])
else
var/tens_place = tens[round(number/10)+1]
//testing(" ([recursion]) tens_place=[round(number/10)+1] = [tens_place]")
if(tens_place!=null)
out.Add(tens_place)
number = number%10
//testing(" ([recursion]) number%10+1 = [number+1] = [digits[number+1]]")
if(number>0)
out.Add(digits[number])
else
var/i=1
while(round(number) > 0)
var/unit_number = number%1000
//testing(" ([recursion]) [number]%1000 = [unit_number] ([i])")
if(unit_number > 0)
if(units[i])
//testing(" ([recursion]) units = [units[i]]")
out = list(units[i]) + out
out = num2words(unit_number, zero, minus, hundred, digits, tens, units, recursion+1) + out
number /= 1000
i++
//testing(" ([recursion]) out=list("+list2text(out,", ")+")")
return out
///mob/verb/test_num2words(var/number as num)
// to_chat(usr, "\"[list2text(num2words(number), " ")]\"")

View File

@@ -1,73 +1,73 @@
// So you can be all 10 SECONDS
#define SECONDS * 10
#define MINUTES * 600
#define HOURS * 36000
//Returns the world time in english
/proc/worldtime2text(timestamp = world.time)
return "[(round(timestamp / 36000) + 12) % 24]:[(timestamp / 600 % 60) < 10 ? add_zero(timestamp / 600 % 60, 1) : timestamp / 600 % 60]"
/proc/formatTimeDuration(var/deciseconds)
var/m = round(deciseconds / 600)
var/s = (deciseconds % 600)/10
var/h = round(m / 60)
m = m % 60
if(h>0)
. += "[h]:"
if(h>0 || m > 0)
. += "[(m<10)?"0":""][m]:"
. += "[(s<10)?"0":""][s]"
/proc/altFormatTimeDuration(var/deciseconds)
var/m = round(deciseconds / 600)
var/s = (deciseconds % 600)/10
var/h = round(m / 60)
m = m % 60
if(h > 0)
. += "[h]h "
if(m > 0)
. += "[m]m "
. += "[s]s"
/proc/time_stamp()
return time2text(world.timeofday, "hh:mm:ss")
/* Preserving this so future generations can see how fucking retarded some people are
/proc/time_stamp()
var/hh = text2num(time2text(world.timeofday, "hh")) // Set the hour
var/mm = text2num(time2text(world.timeofday, "mm")) // Set the minute
var/ss = text2num(time2text(world.timeofday, "ss")) // Set the second
var/ph
var/pm
var/ps
if(hh < 10) ph = "0"
if(mm < 10) pm = "0"
if(ss < 10) ps = "0"
return"[ph][hh]:[pm][mm]:[ps][ss]"
*/
/* Returns 1 if it is the selected month and day */
/proc/isDay(var/month, var/day)
if(isnum(month) && isnum(day))
var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month
var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day
if(month == MM && day == DD)
return 1
// Uncomment this out when debugging!
//else
//return 1
/**
* Returns "watch handle" (really just a timestamp :V)
*/
/proc/start_watch()
return world.timeofday
/**
* Returns number of seconds elapsed.
* @param wh number The "Watch Handle" from start_watch(). (timestamp)
*/
/proc/stop_watch(wh)
// So you can be all 10 SECONDS
#define SECONDS * 10
#define MINUTES * 600
#define HOURS * 36000
//Returns the world time in english
/proc/worldtime2text(timestamp = world.time)
return "[(round(timestamp / 36000) + 12) % 24]:[(timestamp / 600 % 60) < 10 ? add_zero(timestamp / 600 % 60, 1) : timestamp / 600 % 60]"
/proc/formatTimeDuration(var/deciseconds)
var/m = round(deciseconds / 600)
var/s = (deciseconds % 600)/10
var/h = round(m / 60)
m = m % 60
if(h>0)
. += "[h]:"
if(h>0 || m > 0)
. += "[(m<10)?"0":""][m]:"
. += "[(s<10)?"0":""][s]"
/proc/altFormatTimeDuration(var/deciseconds)
var/m = round(deciseconds / 600)
var/s = (deciseconds % 600)/10
var/h = round(m / 60)
m = m % 60
if(h > 0)
. += "[h]h "
if(m > 0)
. += "[m]m "
. += "[s]s"
/proc/time_stamp()
return time2text(world.timeofday, "hh:mm:ss")
/* Preserving this so future generations can see how fucking retarded some people are
/proc/time_stamp()
var/hh = text2num(time2text(world.timeofday, "hh")) // Set the hour
var/mm = text2num(time2text(world.timeofday, "mm")) // Set the minute
var/ss = text2num(time2text(world.timeofday, "ss")) // Set the second
var/ph
var/pm
var/ps
if(hh < 10) ph = "0"
if(mm < 10) pm = "0"
if(ss < 10) ps = "0"
return"[ph][hh]:[pm][mm]:[ps][ss]"
*/
/* Returns 1 if it is the selected month and day */
/proc/isDay(var/month, var/day)
if(isnum(month) && isnum(day))
var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month
var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day
if(month == MM && day == DD)
return 1
// Uncomment this out when debugging!
//else
//return 1
/**
* Returns "watch handle" (really just a timestamp :V)
*/
/proc/start_watch()
return world.timeofday
/**
* Returns number of seconds elapsed.
* @param wh number The "Watch Handle" from start_watch(). (timestamp)
*/
/proc/stop_watch(wh)
return round(0.1*(world.timeofday-wh),0.1)