diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm index a79850b04f..c6ca2cf0ec 100644 --- a/code/__DEFINES/atmospherics.dm +++ b/code/__DEFINES/atmospherics.dm @@ -174,3 +174,5 @@ #define LAVALAND_EQUIPMENT_EFFECT_PRESSURE 50 //what pressure you have to be under to increase the effect of equipment meant for lavaland #define LAVALAND_DEFAULT_ATMOS "o2=14;n2=23;TEMP=300" + +#define THERMAL_ENERGY(gas) (gas.temperature * gas.heat_capacity()) \ No newline at end of file diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index 46d6c2ce5c..75985f9c2e 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -1,544 +1,547 @@ -/* - * Holds procs designed to change one type of value, into another. - * Contains: - * hex2num & num2hex - * file2list - * angle2dir - * angle2text - * worldtime2text - * text2dir_extended & dir2text_short - */ - -//Returns an integer given a hex input, supports negative values "-ff" -//skips preceding invalid characters -//breaks when hittin invalid characters thereafter -/proc/hex2num(hex) - . = 0 - if(istext(hex)) - var/negative = 0 - var/len = length(hex) - for(var/i=1, i<=len, i++) - var/num = text2ascii(hex,i) - switch(num) - if(48 to 57) - num -= 48 //0-9 - if(97 to 102) - num -= 87 //a-f - if(65 to 70) - num -= 55 //A-F - if(45) - negative = 1//- - else - if(num) - break - else - continue - . *= 16 - . += num - if(negative) - . *= -1 - return . - -//Returns the hex value of a decimal number -//len == length of returned string -//if len < 0 then the returned string will be as long as it needs to be to contain the data -//Only supports positive numbers -//if an invalid number is provided, it assumes num==0 -//Note, unlike previous versions, this one works from low to high <-- that way -/proc/num2hex(num, len=2) - if(!isnum(num)) - num = 0 - num = round(abs(num)) - . = "" - var/i=0 - while(1) - if(len<=0) - if(!num) - break - else - if(i>=len) - break - var/remainder = num/16 - num = round(remainder) - remainder = (remainder - num) * 16 - switch(remainder) - if(9,8,7,6,5,4,3,2,1) - . = "[remainder]" + . - if(10,11,12,13,14,15) - . = ascii2text(remainder+87) + . - else - . = "0" + . - i++ - return . - -//Splits the text of a file at seperator and returns them in a list. +/* + * Holds procs designed to change one type of value, into another. + * Contains: + * hex2num & num2hex + * file2list + * angle2dir + * angle2text + * worldtime2text + * text2dir_extended & dir2text_short + */ + +//Returns an integer given a hex input, supports negative values "-ff" +//skips preceding invalid characters +//breaks when hittin invalid characters thereafter +/proc/hex2num(hex) + . = 0 + if(istext(hex)) + var/negative = 0 + var/len = length(hex) + for(var/i=1, i<=len, i++) + var/num = text2ascii(hex,i) + switch(num) + if(48 to 57) + num -= 48 //0-9 + if(97 to 102) + num -= 87 //a-f + if(65 to 70) + num -= 55 //A-F + if(45) + negative = 1//- + else + if(num) + break + else + continue + . *= 16 + . += num + if(negative) + . *= -1 + return . + +//Returns the hex value of a decimal number +//len == length of returned string +//if len < 0 then the returned string will be as long as it needs to be to contain the data +//Only supports positive numbers +//if an invalid number is provided, it assumes num==0 +//Note, unlike previous versions, this one works from low to high <-- that way +/proc/num2hex(num, len=2) + if(!isnum(num)) + num = 0 + num = round(abs(num)) + . = "" + var/i=0 + while(1) + if(len<=0) + if(!num) + break + else + if(i>=len) + break + var/remainder = num/16 + num = round(remainder) + remainder = (remainder - num) * 16 + switch(remainder) + if(9,8,7,6,5,4,3,2,1) + . = "[remainder]" + . + if(10,11,12,13,14,15) + . = ascii2text(remainder+87) + . + else + . = "0" + . + i++ + return . + +//Splits the text of a file at seperator and returns them in a list. /world/proc/file2list(filename, seperator="\n", trim = TRUE) if (trim) return splittext(trim(file2text(filename)),seperator) - return splittext(file2text(filename),seperator) - -//Turns a direction into text -/proc/dir2text(direction) - switch(direction) - if(1) - return "north" - if(2) - return "south" - if(4) - return "east" - if(8) - return "west" - if(5) - return "northeast" - if(6) - return "southeast" - if(9) - return "northwest" - if(10) - return "southwest" - else - return - -//Turns text into proper directions -/proc/text2dir(direction) - switch(uppertext(direction)) - if("NORTH") - return 1 - if("SOUTH") - return 2 - if("EAST") - return 4 - if("WEST") - return 8 - if("NORTHEAST") - return 5 - if("NORTHWEST") - return 9 - if("SOUTHEAST") - return 6 - if("SOUTHWEST") - return 10 - else - return - -//Converts an angle (degrees) into an ss13 direction -/proc/angle2dir(degree) - - degree = SimplifyDegrees(degree) - - if(degree < 45) - return NORTH - if(degree < 90) - return NORTHEAST - if(degree < 135) - return EAST - if(degree < 180) - return SOUTHEAST - if(degree < 225) - return SOUTH - if(degree < 270) - return SOUTHWEST - if(degree < 315) - return WEST - return NORTH|WEST - -//returns the north-zero clockwise angle in degrees, given a direction - -/proc/dir2angle(D) - switch(D) - if(NORTH) - return 0 - if(SOUTH) - return 180 - if(EAST) - return 90 - if(WEST) - return 270 - if(NORTHEAST) - return 45 - if(SOUTHEAST) - return 135 - if(NORTHWEST) - return 315 - if(SOUTHWEST) - return 225 - else - return null - -//Returns the angle in english -/proc/angle2text(degree) - return dir2text(angle2dir(degree)) - -//Converts a blend_mode constant to one acceptable to icon.Blend() -/proc/blendMode2iconMode(blend_mode) - switch(blend_mode) - if(BLEND_MULTIPLY) - return ICON_MULTIPLY - if(BLEND_ADD) - return ICON_ADD - if(BLEND_SUBTRACT) - return ICON_SUBTRACT - else - return ICON_OVERLAY - -//Converts a rights bitfield into a string -/proc/rights2text(rights, seperator="", list/adds, list/subs) - if(rights & R_BUILDMODE) - . += "[seperator]+BUILDMODE" - if(rights & R_ADMIN) - . += "[seperator]+ADMIN" - if(rights & R_BAN) - . += "[seperator]+BAN" - if(rights & R_FUN) - . += "[seperator]+FUN" - if(rights & R_SERVER) - . += "[seperator]+SERVER" - if(rights & R_DEBUG) - . += "[seperator]+DEBUG" - if(rights & R_POSSESS) - . += "[seperator]+POSSESS" - if(rights & R_PERMISSIONS) - . += "[seperator]+PERMISSIONS" - if(rights & R_STEALTH) - . += "[seperator]+STEALTH" - if(rights & R_POLL) - . += "[seperator]+POLL" - if(rights & R_VAREDIT) - . += "[seperator]+VAREDIT" - if(rights & R_SOUNDS) - . += "[seperator]+SOUND" - if(rights & R_SPAWN) - . += "[seperator]+SPAWN" - - for(var/verbpath in adds) - . += "[seperator]+[verbpath]" - for(var/verbpath in subs) - . += "[seperator]-[verbpath]" - return . - -/proc/ui_style2icon(ui_style) - switch(ui_style) - if("Retro") - return 'icons/mob/screen_retro.dmi' - if("Plasmafire") - return 'icons/mob/screen_plasmafire.dmi' - if("Slimecore") - return 'icons/mob/screen_slimecore.dmi' - if("Operative") - return 'icons/mob/screen_operative.dmi' - if("Clockwork") - return 'icons/mob/screen_clockwork.dmi' - else - return 'icons/mob/screen_midnight.dmi' - -//colour formats -/proc/rgb2hsl(red, green, blue) - red /= 255;green /= 255;blue /= 255; - var/max = max(red,green,blue) - var/min = min(red,green,blue) - var/range = max-min - - var/hue=0;var/saturation=0;var/lightness=0; - lightness = (max + min)/2 - if(range != 0) - if(lightness < 0.5) - saturation = range/(max+min) - else - saturation = range/(2-max-min) - - var/dred = ((max-red)/(6*max)) + 0.5 - var/dgreen = ((max-green)/(6*max)) + 0.5 - var/dblue = ((max-blue)/(6*max)) + 0.5 - - if(max==red) - hue = dblue - dgreen - else if(max==green) - hue = dred - dblue + (1/3) - else - hue = dgreen - dred + (2/3) - if(hue < 0) - hue++ - else if(hue > 1) - hue-- - - return list(hue, saturation, lightness) - -/proc/hsl2rgb(hue, saturation, lightness) - var/red;var/green;var/blue; - if(saturation == 0) - red = lightness * 255 - green = red - blue = red - else - var/a;var/b; - if(lightness < 0.5) - b = lightness*(1+saturation) - else - b = (lightness+saturation) - (saturation*lightness) - a = 2*lightness - b - - red = round(255 * hue2rgb(a, b, hue+(1/3))) - green = round(255 * hue2rgb(a, b, hue)) - blue = round(255 * hue2rgb(a, b, hue-(1/3))) - - return list(red, green, blue) - -/proc/hue2rgb(a, b, hue) - if(hue < 0) - hue++ - else if(hue > 1) - hue-- - if(6*hue < 1) - return (a+(b-a)*6*hue) - if(2*hue < 1) - return b - if(3*hue < 2) - return (a+(b-a)*((2/3)-hue)*6) - return a - -// Very ugly, BYOND doesn't support unix time and rounding errors make it really hard to convert it to BYOND time. -// returns "YYYY-MM-DD" by default -/proc/unix2date(timestamp, seperator = "-") - - if(timestamp < 0) - return 0 //Do not accept negative values - - var/year = 1970 //Unix Epoc begins 1970-01-01 - var/dayInSeconds = 86400 //60secs*60mins*24hours - var/daysInYear = 365 //Non Leap Year - var/daysInLYear = daysInYear + 1//Leap year - var/days = round(timestamp / dayInSeconds) //Days passed since UNIX Epoc - var/tmpDays = days + 1 //If passed (timestamp < dayInSeconds), it will return 0, so add 1 - var/monthsInDays = list() //Months will be in here ***Taken from the PHP source code*** - var/month = 1 //This will be the returned MONTH NUMBER. - var/day //This will be the returned day number. - - while(tmpDays > daysInYear) //Start adding years to 1970 - year++ - if(isLeap(year)) - tmpDays -= daysInLYear - else - tmpDays -= daysInYear - - if(isLeap(year)) //The year is a leap year - monthsInDays = list(-1,30,59,90,120,151,181,212,243,273,304,334) - else - monthsInDays = list(0,31,59,90,120,151,181,212,243,273,304,334) - - var/mDays = 0; - var/monthIndex = 0; - - for(var/m in monthsInDays) - monthIndex++ - if(tmpDays > m) - mDays = m - month = monthIndex - - day = tmpDays - mDays //Setup the date - - return "[year][seperator][((month < 10) ? "0[month]" : month)][seperator][((day < 10) ? "0[day]" : day)]" - -/proc/isLeap(y) - return ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0)) - - - -//Turns a Body_parts_covered bitfield into a list of organ/limb names. -//(I challenge you to find a use for this) -/proc/body_parts_covered2organ_names(bpc) - var/list/covered_parts = list() - - if(!bpc) - return 0 - - if(bpc & FULL_BODY) - covered_parts |= list("l_arm","r_arm","head","chest","l_leg","r_leg") - - else - if(bpc & HEAD) - covered_parts |= list("head") - if(bpc & CHEST) - covered_parts |= list("chest") - if(bpc & GROIN) - covered_parts |= list("chest") - - if(bpc & ARMS) - covered_parts |= list("l_arm","r_arm") - else - if(bpc & ARM_LEFT) - covered_parts |= list("l_arm") - if(bpc & ARM_RIGHT) - covered_parts |= list("r_arm") - - if(bpc & HANDS) - covered_parts |= list("l_arm","r_arm") - else - if(bpc & HAND_LEFT) - covered_parts |= list("l_arm") - if(bpc & HAND_RIGHT) - covered_parts |= list("r_arm") - - if(bpc & LEGS) - covered_parts |= list("l_leg","r_leg") - else - if(bpc & LEG_LEFT) - covered_parts |= list("l_leg") - if(bpc & LEG_RIGHT) - covered_parts |= list("r_leg") - - if(bpc & FEET) - covered_parts |= list("l_leg","r_leg") - else - if(bpc & FOOT_LEFT) - covered_parts |= list("l_leg") - if(bpc & FOOT_RIGHT) - covered_parts |= list("r_leg") - - return covered_parts - - - -//adapted from http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ -/proc/heat2colour(temp) - return rgb(heat2colour_r(temp), heat2colour_g(temp), heat2colour_b(temp)) - - -/proc/heat2colour_r(temp) - temp /= 100 - if(temp <= 66) - . = 255 - else - . = max(0, min(255, 329.698727446 * (temp - 60) ** -0.1332047592)) - - -/proc/heat2colour_g(temp) - temp /= 100 - if(temp <= 66) - . = max(0, min(255, 99.4708025861 * log(temp) - 161.1195681661)) - else - . = max(0, min(255, 288.1221685293 * ((temp - 60) ** -0.075148492))) - - -/proc/heat2colour_b(temp) - temp /= 100 - if(temp >= 66) - . = 255 - else - if(temp <= 16) - . = 0 - else - . = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307)) - -/proc/color2hex(color) //web colors - if(!color) - return "#000000" - - switch(color) - if("white") - return "#FFFFFF" - if("black") - return "#000000" - if("gray") - return "#808080" - if("brown") - return "#A52A2A" - if("red") - return "#FF0000" - if("darkred") - return "#8B0000" - if("crimson") - return "#DC143C" - if("orange") - return "#FFA500" - if("yellow") - return "#FFFF00" - if("green") - return "#008000" - if("lime") - return "#00FF00" - if("darkgreen") - return "#006400" - if("cyan") - return "#00FFFF" - if("blue") - return "#0000FF" - if("navy") - return "#000080" - if("teal") - return "#008080" - if("purple") - return "#800080" - if("indigo") - return "#4B0082" - else - return "#FFFFFF" - - -//This is a weird one: -//It returns a list of all var names found in the string -//These vars must be in the [var_name] format -//It's only a proc because it's used in more than one place - -//Takes a string and a datum -//The string is well, obviously the string being checked -//The datum is used as a source for var names, to check validity -//Otherwise every single word could technically be a variable! -/proc/string2listofvars(var/t_string, var/datum/var_source) - if(!t_string || !var_source) - return list() - - . = list() - - var/var_found = findtext(t_string,"\[") //Not the actual variables, just a generic "should we even bother" check - if(var_found) - //Find var names - - // "A dog said hi [name]!" - // splittext() --> list("A dog said hi ","name]!" - // jointext() --> "A dog said hi name]!" - // splittext() --> list("A","dog","said","hi","name]!") - - t_string = replacetext(t_string,"\[","\[ ")//Necessary to resolve "word[var_name]" scenarios - var/list/list_value = splittext(t_string,"\[") - var/intermediate_stage = jointext(list_value, null) - - list_value = splittext(intermediate_stage," ") - for(var/value in list_value) - if(findtext(value,"]")) - value = splittext(value,"]") //"name]!" --> list("name","!") - for(var/A in value) - if(var_source.vars.Find(A)) - . += A - -//assumes format #RRGGBB #rrggbb -/proc/color_hex2num(A) - if(!A) - return 0 - var/R = hex2num(copytext(A,2,4)) - var/G = hex2num(copytext(A,4,6)) - var/B = hex2num(copytext(A,6,0)) - return R+G+B - -//word of warning: using a matrix like this as a color value will simplify it back to a string after being set -/proc/color_hex2color_matrix(string) - var/length = length(string) - if(length != 7 && length != 9) - return color_matrix_identity() - var/r = hex2num(copytext(string, 2, 4))/255 - var/g = hex2num(copytext(string, 4, 6))/255 - var/b = hex2num(copytext(string, 6, 8))/255 - var/a = 1 - if(length == 9) - a = hex2num(copytext(string, 8, 10))/255 - if(!isnum(r) || !isnum(g) || !isnum(b) || !isnum(a)) - return color_matrix_identity() - return list(r,0,0,0, 0,g,0,0, 0,0,b,0, 0,0,0,a, 0,0,0,0) - -//will drop all values not on the diagonal -/proc/color_matrix2color_hex(list/the_matrix) - if(!istype(the_matrix) || the_matrix.len != 20) - return "#ffffffff" - return rgb(the_matrix[1]*255, the_matrix[6]*255, the_matrix[11]*255, the_matrix[16]*255) + return splittext(file2text(filename),seperator) + +//Turns a direction into text +/proc/dir2text(direction) + switch(direction) + if(1) + return "north" + if(2) + return "south" + if(4) + return "east" + if(8) + return "west" + if(5) + return "northeast" + if(6) + return "southeast" + if(9) + return "northwest" + if(10) + return "southwest" + else + return + +//Turns text into proper directions +/proc/text2dir(direction) + switch(uppertext(direction)) + if("NORTH") + return 1 + if("SOUTH") + return 2 + if("EAST") + return 4 + if("WEST") + return 8 + if("NORTHEAST") + return 5 + if("NORTHWEST") + return 9 + if("SOUTHEAST") + return 6 + if("SOUTHWEST") + return 10 + else + return + +//Converts an angle (degrees) into an ss13 direction +/proc/angle2dir(degree) + + degree = SimplifyDegrees(degree) + switch(degree) + if(0 to 22.5) //north requires two angle ranges + return NORTH + if(22.5 to 67.5) //each range covers 45 degrees + return NORTHEAST + if(67.5 to 112.5) + return EAST + if(112.5 to 157.5) + return SOUTHEAST + if(157.5 to 202.5) + return SOUTH + if(202.5 to 247.5) + return SOUTHWEST + if(247.5 to 292.5) + return WEST + if(292.5 to 337.5) + return NORTHWEST + if(337.5 to 360) + return NORTH + +//returns the north-zero clockwise angle in degrees, given a direction + +/proc/dir2angle(D) + switch(D) + if(NORTH) + return 0 + if(SOUTH) + return 180 + if(EAST) + return 90 + if(WEST) + return 270 + if(NORTHEAST) + return 45 + if(SOUTHEAST) + return 135 + if(NORTHWEST) + return 315 + if(SOUTHWEST) + return 225 + else + return null + +//Returns the angle in english +/proc/angle2text(degree) + return dir2text(angle2dir(degree)) + +//Converts a blend_mode constant to one acceptable to icon.Blend() +/proc/blendMode2iconMode(blend_mode) + switch(blend_mode) + if(BLEND_MULTIPLY) + return ICON_MULTIPLY + if(BLEND_ADD) + return ICON_ADD + if(BLEND_SUBTRACT) + return ICON_SUBTRACT + else + return ICON_OVERLAY + +//Converts a rights bitfield into a string +/proc/rights2text(rights, seperator="", list/adds, list/subs) + if(rights & R_BUILDMODE) + . += "[seperator]+BUILDMODE" + if(rights & R_ADMIN) + . += "[seperator]+ADMIN" + if(rights & R_BAN) + . += "[seperator]+BAN" + if(rights & R_FUN) + . += "[seperator]+FUN" + if(rights & R_SERVER) + . += "[seperator]+SERVER" + if(rights & R_DEBUG) + . += "[seperator]+DEBUG" + if(rights & R_POSSESS) + . += "[seperator]+POSSESS" + if(rights & R_PERMISSIONS) + . += "[seperator]+PERMISSIONS" + if(rights & R_STEALTH) + . += "[seperator]+STEALTH" + if(rights & R_POLL) + . += "[seperator]+POLL" + if(rights & R_VAREDIT) + . += "[seperator]+VAREDIT" + if(rights & R_SOUNDS) + . += "[seperator]+SOUND" + if(rights & R_SPAWN) + . += "[seperator]+SPAWN" + + for(var/verbpath in adds) + . += "[seperator]+[verbpath]" + for(var/verbpath in subs) + . += "[seperator]-[verbpath]" + return . + +/proc/ui_style2icon(ui_style) + switch(ui_style) + if("Retro") + return 'icons/mob/screen_retro.dmi' + if("Plasmafire") + return 'icons/mob/screen_plasmafire.dmi' + if("Slimecore") + return 'icons/mob/screen_slimecore.dmi' + if("Operative") + return 'icons/mob/screen_operative.dmi' + if("Clockwork") + return 'icons/mob/screen_clockwork.dmi' + else + return 'icons/mob/screen_midnight.dmi' + +//colour formats +/proc/rgb2hsl(red, green, blue) + red /= 255;green /= 255;blue /= 255; + var/max = max(red,green,blue) + var/min = min(red,green,blue) + var/range = max-min + + var/hue=0;var/saturation=0;var/lightness=0; + lightness = (max + min)/2 + if(range != 0) + if(lightness < 0.5) + saturation = range/(max+min) + else + saturation = range/(2-max-min) + + var/dred = ((max-red)/(6*max)) + 0.5 + var/dgreen = ((max-green)/(6*max)) + 0.5 + var/dblue = ((max-blue)/(6*max)) + 0.5 + + if(max==red) + hue = dblue - dgreen + else if(max==green) + hue = dred - dblue + (1/3) + else + hue = dgreen - dred + (2/3) + if(hue < 0) + hue++ + else if(hue > 1) + hue-- + + return list(hue, saturation, lightness) + +/proc/hsl2rgb(hue, saturation, lightness) + var/red;var/green;var/blue; + if(saturation == 0) + red = lightness * 255 + green = red + blue = red + else + var/a;var/b; + if(lightness < 0.5) + b = lightness*(1+saturation) + else + b = (lightness+saturation) - (saturation*lightness) + a = 2*lightness - b + + red = round(255 * hue2rgb(a, b, hue+(1/3))) + green = round(255 * hue2rgb(a, b, hue)) + blue = round(255 * hue2rgb(a, b, hue-(1/3))) + + return list(red, green, blue) + +/proc/hue2rgb(a, b, hue) + if(hue < 0) + hue++ + else if(hue > 1) + hue-- + if(6*hue < 1) + return (a+(b-a)*6*hue) + if(2*hue < 1) + return b + if(3*hue < 2) + return (a+(b-a)*((2/3)-hue)*6) + return a + +// Very ugly, BYOND doesn't support unix time and rounding errors make it really hard to convert it to BYOND time. +// returns "YYYY-MM-DD" by default +/proc/unix2date(timestamp, seperator = "-") + + if(timestamp < 0) + return 0 //Do not accept negative values + + var/year = 1970 //Unix Epoc begins 1970-01-01 + var/dayInSeconds = 86400 //60secs*60mins*24hours + var/daysInYear = 365 //Non Leap Year + var/daysInLYear = daysInYear + 1//Leap year + var/days = round(timestamp / dayInSeconds) //Days passed since UNIX Epoc + var/tmpDays = days + 1 //If passed (timestamp < dayInSeconds), it will return 0, so add 1 + var/monthsInDays = list() //Months will be in here ***Taken from the PHP source code*** + var/month = 1 //This will be the returned MONTH NUMBER. + var/day //This will be the returned day number. + + while(tmpDays > daysInYear) //Start adding years to 1970 + year++ + if(isLeap(year)) + tmpDays -= daysInLYear + else + tmpDays -= daysInYear + + if(isLeap(year)) //The year is a leap year + monthsInDays = list(-1,30,59,90,120,151,181,212,243,273,304,334) + else + monthsInDays = list(0,31,59,90,120,151,181,212,243,273,304,334) + + var/mDays = 0; + var/monthIndex = 0; + + for(var/m in monthsInDays) + monthIndex++ + if(tmpDays > m) + mDays = m + month = monthIndex + + day = tmpDays - mDays //Setup the date + + return "[year][seperator][((month < 10) ? "0[month]" : month)][seperator][((day < 10) ? "0[day]" : day)]" + +/proc/isLeap(y) + return ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0)) + + + +//Turns a Body_parts_covered bitfield into a list of organ/limb names. +//(I challenge you to find a use for this) +/proc/body_parts_covered2organ_names(bpc) + var/list/covered_parts = list() + + if(!bpc) + return 0 + + if(bpc & FULL_BODY) + covered_parts |= list("l_arm","r_arm","head","chest","l_leg","r_leg") + + else + if(bpc & HEAD) + covered_parts |= list("head") + if(bpc & CHEST) + covered_parts |= list("chest") + if(bpc & GROIN) + covered_parts |= list("chest") + + if(bpc & ARMS) + covered_parts |= list("l_arm","r_arm") + else + if(bpc & ARM_LEFT) + covered_parts |= list("l_arm") + if(bpc & ARM_RIGHT) + covered_parts |= list("r_arm") + + if(bpc & HANDS) + covered_parts |= list("l_arm","r_arm") + else + if(bpc & HAND_LEFT) + covered_parts |= list("l_arm") + if(bpc & HAND_RIGHT) + covered_parts |= list("r_arm") + + if(bpc & LEGS) + covered_parts |= list("l_leg","r_leg") + else + if(bpc & LEG_LEFT) + covered_parts |= list("l_leg") + if(bpc & LEG_RIGHT) + covered_parts |= list("r_leg") + + if(bpc & FEET) + covered_parts |= list("l_leg","r_leg") + else + if(bpc & FOOT_LEFT) + covered_parts |= list("l_leg") + if(bpc & FOOT_RIGHT) + covered_parts |= list("r_leg") + + return covered_parts + + + +//adapted from http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ +/proc/heat2colour(temp) + return rgb(heat2colour_r(temp), heat2colour_g(temp), heat2colour_b(temp)) + + +/proc/heat2colour_r(temp) + temp /= 100 + if(temp <= 66) + . = 255 + else + . = max(0, min(255, 329.698727446 * (temp - 60) ** -0.1332047592)) + + +/proc/heat2colour_g(temp) + temp /= 100 + if(temp <= 66) + . = max(0, min(255, 99.4708025861 * log(temp) - 161.1195681661)) + else + . = max(0, min(255, 288.1221685293 * ((temp - 60) ** -0.075148492))) + + +/proc/heat2colour_b(temp) + temp /= 100 + if(temp >= 66) + . = 255 + else + if(temp <= 16) + . = 0 + else + . = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307)) + +/proc/color2hex(color) //web colors + if(!color) + return "#000000" + + switch(color) + if("white") + return "#FFFFFF" + if("black") + return "#000000" + if("gray") + return "#808080" + if("brown") + return "#A52A2A" + if("red") + return "#FF0000" + if("darkred") + return "#8B0000" + if("crimson") + return "#DC143C" + if("orange") + return "#FFA500" + if("yellow") + return "#FFFF00" + if("green") + return "#008000" + if("lime") + return "#00FF00" + if("darkgreen") + return "#006400" + if("cyan") + return "#00FFFF" + if("blue") + return "#0000FF" + if("navy") + return "#000080" + if("teal") + return "#008080" + if("purple") + return "#800080" + if("indigo") + return "#4B0082" + else + return "#FFFFFF" + + +//This is a weird one: +//It returns a list of all var names found in the string +//These vars must be in the [var_name] format +//It's only a proc because it's used in more than one place + +//Takes a string and a datum +//The string is well, obviously the string being checked +//The datum is used as a source for var names, to check validity +//Otherwise every single word could technically be a variable! +/proc/string2listofvars(var/t_string, var/datum/var_source) + if(!t_string || !var_source) + return list() + + . = list() + + var/var_found = findtext(t_string,"\[") //Not the actual variables, just a generic "should we even bother" check + if(var_found) + //Find var names + + // "A dog said hi [name]!" + // splittext() --> list("A dog said hi ","name]!" + // jointext() --> "A dog said hi name]!" + // splittext() --> list("A","dog","said","hi","name]!") + + t_string = replacetext(t_string,"\[","\[ ")//Necessary to resolve "word[var_name]" scenarios + var/list/list_value = splittext(t_string,"\[") + var/intermediate_stage = jointext(list_value, null) + + list_value = splittext(intermediate_stage," ") + for(var/value in list_value) + if(findtext(value,"]")) + value = splittext(value,"]") //"name]!" --> list("name","!") + for(var/A in value) + if(var_source.vars.Find(A)) + . += A + +//assumes format #RRGGBB #rrggbb +/proc/color_hex2num(A) + if(!A) + return 0 + var/R = hex2num(copytext(A,2,4)) + var/G = hex2num(copytext(A,4,6)) + var/B = hex2num(copytext(A,6,0)) + return R+G+B + +//word of warning: using a matrix like this as a color value will simplify it back to a string after being set +/proc/color_hex2color_matrix(string) + var/length = length(string) + if(length != 7 && length != 9) + return color_matrix_identity() + var/r = hex2num(copytext(string, 2, 4))/255 + var/g = hex2num(copytext(string, 4, 6))/255 + var/b = hex2num(copytext(string, 6, 8))/255 + var/a = 1 + if(length == 9) + a = hex2num(copytext(string, 8, 10))/255 + if(!isnum(r) || !isnum(g) || !isnum(b) || !isnum(a)) + return color_matrix_identity() + return list(r,0,0,0, 0,g,0,0, 0,0,b,0, 0,0,0,a, 0,0,0,0) + +//will drop all values not on the diagonal +/proc/color_matrix2color_hex(list/the_matrix) + if(!istype(the_matrix) || the_matrix.len != 20) + return "#ffffffff" + return rgb(the_matrix[1]*255, the_matrix[6]*255, the_matrix[11]*255, the_matrix[16]*255) /proc/type2parent(child) var/string_type = "[child]" diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 987818924f..4c0eeccf1d 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -20,7 +20,7 @@ return SendSignal(COMSIG_PARENT_ATTACKBY, W, user, params) /obj/attackby(obj/item/I, mob/living/user, params) - return ..() || I.attack_obj(src, user) + return ..() || (can_be_hit && I.attack_obj(src, user)) /mob/living/attackby(obj/item/I, mob/living/user, params) user.changeNext_move(CLICK_CD_MELEE) diff --git a/code/datums/components/paintable.dm b/code/datums/components/paintable.dm new file mode 100644 index 0000000000..2ea94334d0 --- /dev/null +++ b/code/datums/components/paintable.dm @@ -0,0 +1,29 @@ +/datum/component/spraycan_paintable + var/current_paint + +/datum/component/spraycan_paintable/Initialize() + RegisterSignal(COMSIG_PARENT_ATTACKBY, .proc/Repaint) + +/datum/component/spraycan_paintable/Destroy() + RemoveCurrentCoat() + return ..() + +/datum/component/spraycan_paintable/proc/RemoveCurrentCoat() + var/atom/A = parent + A.remove_atom_colour(FIXED_COLOUR_PRIORITY, current_paint) + +/datum/component/spraycan_paintable/proc/Repaint(obj/item/toy/crayon/spraycan/spraycan, mob/living/user) + if(!istype(spraycan) || user.a_intent == INTENT_HARM) + return FALSE + . = TRUE + if(spraycan.is_capped) + to_chat(user, "Take the cap off first!") + return + RemoveCurrentCoat() + if(spraycan.use_charges(user, 2)) + var/colour = spraycan.paint_color + current_paint = colour + var/atom/A = parent + A.add_atom_colour(colour, FIXED_COLOUR_PRIORITY) + playsound(spraycan, 'sound/effects/spray.ogg', 5, 1, 5) + to_chat(user, "You spray [spraycan] on [A], painting it.") diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm index 455ec0f62a..88e6148c5f 100644 --- a/code/datums/emotes.dm +++ b/code/datums/emotes.dm @@ -42,7 +42,7 @@ I.trigger(key, L) if(!msg) - return FALSE + return user.log_message(msg, INDIVIDUAL_EMOTE_LOG) msg = "[user] " + msg @@ -91,21 +91,22 @@ /datum/emote/proc/select_param(mob/user, params) return replacetext(message_param, "%t", params) -/datum/emote/proc/can_run_emote(mob/user, help_check) +/datum/emote/proc/can_run_emote(mob/user, status_check = TRUE) . = TRUE if(!is_type_in_typecache(user, mob_type_allowed_typecache)) return FALSE if(is_type_in_typecache(user, mob_type_blacklist_typecache)) return FALSE - if(!help_check) + if(status_check) if(user.stat > stat_allowed || (user.status_flags & FAKEDEATH)) + to_chat(user, "You cannot [key] while unconscious.") return FALSE - if(restraint_check && user.restrained()) + if(restraint_check && (user.restrained() || user.buckled)) + to_chat(user, "You cannot [key] while restrained.") return FALSE if(user.reagents && user.reagents.has_reagent("mimesbane")) return FALSE - /datum/emote/sound var/sound //Sound to play when emote is called var/vary = FALSE //used for the honk borg emote diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index b062b8f054..222ce73f92 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -449,10 +449,11 @@ GLOBAL_LIST_EMPTY(teleportlocs) if(!L.client.played) SEND_SOUND(L, sound(sound, repeat = 0, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE)) - L.client.played = 1 - sleep(600) //ewww - this is very very bad - if(L.&& L.client) - L.client.played = 0 + L.client.played = TRUE + addtimer(CALLBACK(L.client, /client/proc/ResetAmbiencePlayed), 600) + +/client/proc/ResetAmbiencePlayed() + played = FALSE /atom/proc/has_gravity(turf/T) if(!T || !isturf(T)) diff --git a/code/game/gamemodes/clock_cult/clock_structures/ocular_warden.dm b/code/game/gamemodes/clock_cult/clock_structures/ocular_warden.dm index cdff486eea..364c967ae9 100644 --- a/code/game/gamemodes/clock_cult/clock_structures/ocular_warden.dm +++ b/code/game/gamemodes/clock_cult/clock_structures/ocular_warden.dm @@ -11,7 +11,7 @@ break_message = "The warden's eye gives a glare of utter hate before falling dark!" debris = list(/obj/item/clockwork/component/belligerent_eye/blind_eye = 1) resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - var/damage_per_tick = 2.5 + var/damage_per_tick = 3 var/sight_range = 3 var/atom/movable/target var/list/idle_messages = list(" sulkily glares around.", " lazily drifts from side to side.", " looks around for something to burn.", " slowly turns in circles.") @@ -141,8 +141,12 @@ . = 1 if(target) for(var/turf/T in getline(src, target)) + if(T.density) + . -= 0.1 + continue for(var/obj/structure/O in T) - if(O.density) - . -= 0.15 + if(O != src && O.density) + . -= 0.1 + break . -= (get_dist(src, target) * 0.05) . = max(., 0.1) //The lowest damage a warden can do is 10% of its normal amount (0.25 by default) diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index b3a7f99758..d4a06d0580 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -1009,8 +1009,9 @@ IF YOU MODIFY THE PRODUCTS LIST OF A MACHINE, MAKE SURE TO UPDATE ITS RESUPPLY C /obj/item/clothing/mask/joy = 1, /obj/item/clothing/head/cueball = 1, /obj/item/clothing/under/scratch = 1, - /obj/item/clothing/under/sailor = 1, - /obj/item/clothing/ears/headphones = 2) + /obj/item/clothing/under/sailor = 1, + /obj/item/clothing/ears/headphones = 2, + /obj/item/clothing/head/wig/random = 3) contraband = list(/obj/item/clothing/suit/judgerobe = 1, /obj/item/clothing/head/powdered_wig = 1, /obj/item/gun/magic/wand = 2, /obj/item/clothing/glasses/sunglasses/garb = 2, /obj/item/clothing/glasses/sunglasses/blindfold = 1, /obj/item/clothing/mask/muzzle = 2) premium = list(/obj/item/clothing/suit/pirate/captain = 2, /obj/item/clothing/head/pirate/captain = 2, /obj/item/clothing/head/helmet/roman = 1, /obj/item/clothing/head/helmet/roman/legionaire = 1, /obj/item/clothing/under/roman = 1, /obj/item/clothing/shoes/roman = 1, /obj/item/shield/riot/roman = 1, /obj/item/skub = 1) refill_canister = /obj/item/vending_refill/autodrobe diff --git a/code/game/objects/effects/effects.dm b/code/game/objects/effects/effects.dm index 4406c32fbd..d5f8e28d2a 100644 --- a/code/game/objects/effects/effects.dm +++ b/code/game/objects/effects/effects.dm @@ -4,9 +4,7 @@ /obj/effect icon = 'icons/effects/effects.dmi' resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF - -/obj/effect/attackby(obj/item/I, mob/living/user, params) - return + can_be_hit = FALSE /obj/effect/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) return @@ -57,4 +55,4 @@ return /obj/effect/abstract/singularity_act() - return \ No newline at end of file + return diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 072896ac01..362148c801 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -26,6 +26,8 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) max_integrity = 200 + can_be_hit = FALSE + var/hitsound = null var/usesound = null var/throwhitsound = null @@ -322,6 +324,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) // Due to storage type consolidation this should get used more now. // I have cleaned it up a little, but it could probably use more. -Sayu // The lack of ..() is intentional, do not add one +// added one, fuck the police /obj/item/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/storage)) var/obj/item/storage/S = W @@ -346,9 +349,12 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE) qdel(progress) to_chat(user, "You put everything you could [S.preposition] [S].") + return else if(S.can_be_inserted(src)) S.handle_item_insertion(src) + return + return ..() /obj/item/proc/handle_mass_pickup(obj/item/storage/S, list/things, atom/thing_loc, list/rejections, datum/progressbar/progress) for(var/obj/item/I in things) diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 23905200e9..882335d5e7 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -107,20 +107,19 @@ var/amount = weight * units_per_weight reagents.add_reagent(reagent, amount) -/obj/item/toy/crayon/proc/use_charges(amount) +/obj/item/toy/crayon/proc/use_charges(mob/user, amount = 1, requires_full = TRUE) // Returns number of charges actually used - switch(paint_mode) - if(PAINT_LARGE_HORIZONTAL) - amount *= 3 - if(charges == -1) . = amount refill() else - . = min(charges_left, amount) - charges_left -= . + if(check_empty(user, amount, requires_full)) + return 0 + else + . = min(charges_left, amount) + charges_left -= . -/obj/item/toy/crayon/proc/check_empty(mob/user) +/obj/item/toy/crayon/proc/check_empty(mob/user, amount = 1, requires_full = TRUE) // When eating a crayon, check_empty() can be called twice producing // two messages unless we check for being deleted first if(QDELETED(src)) @@ -131,10 +130,13 @@ if(charges == -1) . = FALSE else if(!charges_left) - to_chat(user, "There is no more of \the [src.name] left!") + to_chat(user, "There is no more of [src] left!") if(self_contained) qdel(src) . = TRUE + else if(charges_left < amount && requires_full) + to_chat(user, "There is not enough of [src] left!") + . = TRUE /obj/item/toy/crayon/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state) // tgui is a plague upon this codebase @@ -298,7 +300,7 @@ var/takes_time = !instant var/wait_time = 50 - if(PAINT_LARGE_HORIZONTAL) + if(paint_mode == PAINT_LARGE_HORIZONTAL) wait_time *= 3 if(takes_time) @@ -343,7 +345,7 @@ var/cost = 1 if(paint_mode == PAINT_LARGE_HORIZONTAL) cost = 5 - . = use_charges(cost) + . = use_charges(user, cost) var/fraction = min(1, . / reagents.maximum_volume) if(affected_turfs.len) fraction /= affected_turfs.len @@ -355,7 +357,7 @@ /obj/item/toy/crayon/attack(mob/M, mob/user) if(edible && (M == user)) to_chat(user, "You take a bite of the [src.name]. Delicious!") - var/eaten = use_charges(5) + var/eaten = use_charges(user, 5, FALSE) if(check_empty(user)) //Prevents divsion by zero return var/fraction = min(eaten / reagents.total_volume, 1) @@ -527,7 +529,7 @@ H.lip_style = "spray_face" H.lip_color = paint_color H.update_body() - var/used = use_charges(10) + var/used = use_charges(user, 10, FALSE) var/fraction = min(1, used / reagents.maximum_volume) reagents.reaction(user, VAPOR, fraction * volume_multiplier) reagents.trans_to(user, used, volume_multiplier) @@ -583,7 +585,7 @@ H.update_body() // Caution, spray cans contain inflammable substances - . = use_charges(10) + . = use_charges(user, 10, FALSE) var/fraction = min(1, . / reagents.maximum_volume) reagents.reaction(C, VAPOR, fraction * volume_multiplier) @@ -596,7 +598,7 @@ target.set_opacity(255) else target.set_opacity(initial(target.opacity)) - . = use_charges(2) + . = use_charges(user, 2) var/fraction = min(1, . / reagents.maximum_volume) reagents.reaction(target, TOUCH, fraction * volume_multiplier) reagents.trans_to(target, ., volume_multiplier) diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 6e7ffddeb3..7cef6094ea 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -151,7 +151,11 @@ if(!building_checks(R, multiplier)) return - var/obj/O = new R.result_type() + var/obj/O + if(R.max_res_amount > 1) //Is it a stack? + O = new R.result_type(usr.drop_location(), R.res_amount * multiplier) + else + O = new R.result_type(usr.drop_location()) O.setDir(usr.dir) use(R.req_amount * multiplier) @@ -164,16 +168,11 @@ W.ini_dir = W.dir //END: oh fuck i'm so sorry - //is it a stack ? - if (R.max_res_amount > 1) - var/obj/item/stack/new_item = O - new_item.amount = R.res_amount*multiplier - new_item.update_icon() + if (QDELETED(O)) + return //It's a stack and has already been merged if (isitem(O)) usr.put_in_hands(O) - else - O.forceMove(usr.drop_location()) O.add_fingerprint(usr) //BubbleWrap - so newly formed boxes are empty diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm deleted file mode 100644 index f8a020b677..0000000000 --- a/code/game/objects/items/weapons/storage/belt.dm +++ /dev/null @@ -1,531 +0,0 @@ -/obj/item/weapon/storage/belt - name = "belt" - desc = "Can hold various things." - icon = 'icons/obj/clothing/belts.dmi' - icon_state = "utilitybelt" - item_state = "utility" - slot_flags = SLOT_BELT - attack_verb = list("whipped", "lashed", "disciplined") - max_integrity = 300 - var/content_overlays = FALSE //If this is true, the belt will gain overlays based on what it's holding - -/obj/item/weapon/storage/belt/update_icon() - cut_overlays() - if(content_overlays) - for(var/obj/item/I in contents) - var/mutable_appearance/M = I.get_belt_overlay() - add_overlay(M) - ..() - -/obj/item/weapon/storage/belt/Initialize() - . = ..() - update_icon() - -/obj/item/weapon/storage/belt/utility - name = "toolbelt" //Carn: utility belt is nicer, but it bamboozles the text parsing. - desc = "Holds tools." - icon_state = "utilitybelt" - item_state = "utility" - can_hold = list( - /obj/item/weapon/crowbar, - /obj/item/weapon/screwdriver, - /obj/item/weapon/weldingtool, - /obj/item/weapon/wirecutters, - /obj/item/weapon/wrench, - /obj/item/device/multitool, - /obj/item/device/flashlight, - /obj/item/stack/cable_coil, - /obj/item/device/t_scanner, - /obj/item/device/analyzer, - /obj/item/weapon/extinguisher/mini, - /obj/item/device/radio, - /obj/item/clothing/gloves - ) - content_overlays = TRUE - -/obj/item/weapon/storage/belt/utility/chief - name = "\improper Chief Engineer's toolbelt" //"the Chief Engineer's toolbelt", because "Chief Engineer's toolbelt" is not a proper noun - desc = "Holds tools, looks snazzy." - icon_state = "utilitybelt_ce" - item_state = "utility_ce" - -/obj/item/weapon/storage/belt/utility/chief/full/PopulateContents() - new /obj/item/weapon/screwdriver/power(src) - new /obj/item/weapon/crowbar/power(src) - new /obj/item/weapon/weldingtool/experimental(src)//This can be changed if this is too much - new /obj/item/device/multitool(src) - new /obj/item/stack/cable_coil(src,30,pick("red","yellow","orange")) - new /obj/item/weapon/extinguisher/mini(src) - new /obj/item/device/analyzer(src) - //much roomier now that we've managed to remove two tools - - -/obj/item/weapon/storage/belt/utility/full/PopulateContents() - new /obj/item/weapon/screwdriver(src) - new /obj/item/weapon/wrench(src) - new /obj/item/weapon/weldingtool(src) - new /obj/item/weapon/crowbar(src) - new /obj/item/weapon/wirecutters(src) - new /obj/item/device/multitool(src) - new /obj/item/stack/cable_coil(src,30,pick("red","yellow","orange")) - -/obj/item/weapon/storage/belt/utility/full/engi/PopulateContents() - new /obj/item/weapon/screwdriver(src) - new /obj/item/weapon/wrench(src) - new /obj/item/weapon/weldingtool/largetank(src) - new /obj/item/weapon/crowbar(src) - new /obj/item/weapon/wirecutters(src) - new /obj/item/device/multitool(src) - new /obj/item/stack/cable_coil(src,30,pick("red","yellow","orange")) - - -/obj/item/weapon/storage/belt/utility/atmostech/PopulateContents() - new /obj/item/weapon/screwdriver(src) - new /obj/item/weapon/wrench(src) - new /obj/item/weapon/weldingtool(src) - new /obj/item/weapon/crowbar(src) - new /obj/item/weapon/wirecutters(src) - new /obj/item/device/t_scanner(src) - new /obj/item/weapon/extinguisher/mini(src) - - -/obj/item/weapon/storage/belt/utility/servant/PopulateContents() - new /obj/item/weapon/screwdriver/brass(src) - new /obj/item/weapon/wirecutters/brass(src) - new /obj/item/weapon/wrench/brass(src) - new /obj/item/weapon/crowbar/brass(src) - new /obj/item/weapon/weldingtool/experimental/brass(src) - new /obj/item/device/multitool(src) - new /obj/item/stack/cable_coil(src, 30, "yellow") - - -/obj/item/weapon/storage/belt/medical - name = "medical belt" - desc = "Can hold various medical equipment." - icon_state = "medicalbelt" - item_state = "medical" - max_w_class = WEIGHT_CLASS_BULKY - can_hold = list( - /obj/item/device/healthanalyzer, - /obj/item/weapon/dnainjector, - /obj/item/weapon/reagent_containers/dropper, - /obj/item/weapon/reagent_containers/glass/beaker, - /obj/item/weapon/reagent_containers/glass/bottle, - /obj/item/weapon/reagent_containers/pill, - /obj/item/weapon/reagent_containers/syringe, - /obj/item/weapon/lighter, - /obj/item/weapon/storage/fancy/cigarettes, - /obj/item/weapon/storage/pill_bottle, - /obj/item/stack/medical, - /obj/item/device/flashlight/pen, - /obj/item/weapon/extinguisher/mini, - /obj/item/weapon/reagent_containers/hypospray, - /obj/item/device/sensor_device, - /obj/item/device/radio, - /obj/item/clothing/gloves/, - /obj/item/weapon/lazarus_injector, - /obj/item/weapon/bikehorn/rubberducky, - /obj/item/clothing/mask/surgical, - /obj/item/clothing/mask/breath, - /obj/item/clothing/mask/breath/medical, - /obj/item/weapon/surgical_drapes, //for true paramedics - /obj/item/weapon/scalpel, - /obj/item/weapon/circular_saw, - /obj/item/weapon/surgicaldrill, - /obj/item/weapon/retractor, - /obj/item/weapon/cautery, - /obj/item/weapon/hemostat, - /obj/item/device/geiger_counter, - /obj/item/clothing/neck/stethoscope, - /obj/item/weapon/stamp, - /obj/item/clothing/glasses, - /obj/item/weapon/wrench/medical, - /obj/item/clothing/mask/muzzle, - /obj/item/weapon/storage/bag/chemistry, - /obj/item/weapon/storage/bag/bio, - /obj/item/weapon/reagent_containers/blood, - /obj/item/weapon/tank/internals/emergency_oxygen - ) - - -/obj/item/weapon/storage/belt/security - name = "security belt" - desc = "Can hold security gear like handcuffs and flashes." - icon_state = "securitybelt" - item_state = "security"//Could likely use a better one. - storage_slots = 5 - max_w_class = WEIGHT_CLASS_NORMAL //Because the baton wouldn't fit otherwise. - Neerti - can_hold = list( - /obj/item/weapon/melee/baton, - /obj/item/weapon/melee/classic_baton, - /obj/item/weapon/grenade, - /obj/item/weapon/reagent_containers/spray/pepper, - /obj/item/weapon/restraints/handcuffs, - /obj/item/device/assembly/flash/handheld, - /obj/item/clothing/glasses, - /obj/item/ammo_casing/shotgun, - /obj/item/ammo_box, - /obj/item/weapon/reagent_containers/food/snacks/donut, - /obj/item/weapon/reagent_containers/food/snacks/donut/jelly, - /obj/item/weapon/kitchen/knife/combat, - /obj/item/device/flashlight/seclite, - /obj/item/weapon/melee/classic_baton/telescopic, - /obj/item/device/radio, - /obj/item/clothing/gloves/, - /obj/item/weapon/restraints/legcuffs/bola - ) - content_overlays = TRUE - -/obj/item/weapon/storage/belt/security/full/PopulateContents() - new /obj/item/weapon/reagent_containers/spray/pepper(src) - new /obj/item/weapon/restraints/handcuffs(src) - new /obj/item/weapon/grenade/flashbang(src) - new /obj/item/device/assembly/flash/handheld(src) - new /obj/item/weapon/melee/baton/loaded(src) - update_icon() - - -/obj/item/weapon/storage/belt/mining - name = "explorer's webbing" - desc = "A versatile chest rig, cherished by miners and hunters alike." - icon_state = "explorer1" - item_state = "explorer1" - storage_slots = 6 - w_class = WEIGHT_CLASS_BULKY - max_w_class = WEIGHT_CLASS_BULKY //Pickaxes are big. - max_combined_w_class = 20 //Not an issue with this whitelist, probably. - can_hold = list( - /obj/item/weapon/crowbar, - /obj/item/weapon/screwdriver, - /obj/item/weapon/weldingtool, - /obj/item/weapon/wirecutters, - /obj/item/weapon/wrench, - /obj/item/device/flashlight, - /obj/item/stack/cable_coil, - /obj/item/device/analyzer, - /obj/item/weapon/extinguisher/mini, - /obj/item/device/radio, - /obj/item/clothing/gloves, - /obj/item/weapon/resonator, - /obj/item/device/mining_scanner, - /obj/item/weapon/pickaxe, - /obj/item/stack/sheet/animalhide, - /obj/item/stack/sheet/sinew, - /obj/item/stack/sheet/bone, - /obj/item/weapon/lighter, - /obj/item/weapon/storage/fancy/cigarettes, - /obj/item/weapon/reagent_containers/food/drinks/bottle, - /obj/item/stack/medical, - /obj/item/weapon/kitchen/knife, - /obj/item/weapon/reagent_containers/hypospray, - /obj/item/device/gps, - /obj/item/weapon/storage/bag/ore, - /obj/item/weapon/survivalcapsule, - /obj/item/device/t_scanner/adv_mining_scanner, - /obj/item/weapon/reagent_containers/pill, - /obj/item/weapon/storage/pill_bottle, - /obj/item/weapon/ore, - /obj/item/weapon/reagent_containers/food/drinks, - /obj/item/organ/regenerative_core, - /obj/item/device/wormhole_jaunter, - /obj/item/weapon/storage/bag/plants, - /obj/item/stack/marker_beacon - ) - - -/obj/item/weapon/storage/belt/mining/vendor - contents = newlist(/obj/item/weapon/survivalcapsule) - -/obj/item/weapon/storage/belt/mining/alt - icon_state = "explorer2" - item_state = "explorer2" - -/obj/item/weapon/storage/belt/mining/primitive - name = "hunter's belt" - desc = "A versatile belt, woven from sinew." - storage_slots = 5 - icon_state = "ebelt" - item_state = "ebelt" - -/obj/item/weapon/storage/belt/soulstone - name = "soul stone belt" - desc = "Designed for ease of access to the shards during a fight, as to not let a single enemy spirit slip away" - icon_state = "soulstonebelt" - item_state = "soulstonebelt" - storage_slots = 6 - can_hold = list( - /obj/item/device/soulstone - ) - -/obj/item/weapon/storage/belt/soulstone/full/PopulateContents() - for(var/i in 1 to 6) - new /obj/item/device/soulstone(src) - -/obj/item/weapon/storage/belt/champion - name = "championship belt" - desc = "Proves to the world that you are the strongest!" - icon_state = "championbelt" - item_state = "champion" - materials = list(MAT_GOLD=400) - storage_slots = 1 - can_hold = list( - /obj/item/clothing/mask/luchador - ) - -/obj/item/weapon/storage/belt/military - name = "chest rig" - desc = "A set of tactical webbing worn by Syndicate boarding parties." - icon_state = "militarywebbing" - item_state = "militarywebbing" - max_w_class = WEIGHT_CLASS_SMALL - -/obj/item/weapon/storage/belt/military/abductor - name = "agent belt" - desc = "A belt used by abductor agents." - icon = 'icons/obj/abductor.dmi' - icon_state = "belt" - item_state = "security" - -/obj/item/weapon/storage/belt/military/abductor/full/PopulateContents() - new /obj/item/weapon/screwdriver/abductor(src) - new /obj/item/weapon/wrench/abductor(src) - new /obj/item/weapon/weldingtool/abductor(src) - new /obj/item/weapon/crowbar/abductor(src) - new /obj/item/weapon/wirecutters/abductor(src) - new /obj/item/device/multitool/abductor(src) - new /obj/item/stack/cable_coil(src,30,"white") - - -/obj/item/weapon/storage/belt/military/army - name = "army belt" - desc = "A belt used by military forces." - icon_state = "grenadebeltold" - item_state = "security" - -/obj/item/weapon/storage/belt/military/assault - name = "assault belt" - desc = "A tactical assault belt." - icon_state = "assaultbelt" - item_state = "security" - storage_slots = 6 - -/obj/item/weapon/storage/belt/grenade - name = "grenadier belt" - desc = "A belt for holding grenades." - icon_state = "grenadebeltnew" - item_state = "security" - max_w_class = WEIGHT_CLASS_BULKY - display_contents_with_number = TRUE - storage_slots = 30 - max_combined_w_class = 60 //needs to be this high - can_hold = list( - /obj/item/weapon/grenade, - /obj/item/weapon/screwdriver, - /obj/item/weapon/lighter, - /obj/item/device/multitool, - /obj/item/weapon/reagent_containers/food/drinks/bottle/molotov, - /obj/item/weapon/grenade/plastic/c4, - ) -/obj/item/weapon/storage/belt/grenade/full/PopulateContents() - new /obj/item/weapon/grenade/flashbang(src) - new /obj/item/weapon/grenade/smokebomb(src) - new /obj/item/weapon/grenade/smokebomb(src) - new /obj/item/weapon/grenade/smokebomb(src) - new /obj/item/weapon/grenade/smokebomb(src) - new /obj/item/weapon/grenade/empgrenade(src) - new /obj/item/weapon/grenade/empgrenade(src) - new /obj/item/weapon/grenade/syndieminibomb/concussion/frag(src) - new /obj/item/weapon/grenade/syndieminibomb/concussion/frag(src) - new /obj/item/weapon/grenade/syndieminibomb/concussion/frag(src) - new /obj/item/weapon/grenade/syndieminibomb/concussion/frag(src) - new /obj/item/weapon/grenade/syndieminibomb/concussion/frag(src) - new /obj/item/weapon/grenade/syndieminibomb/concussion/frag(src) - new /obj/item/weapon/grenade/syndieminibomb/concussion/frag(src) - new /obj/item/weapon/grenade/syndieminibomb/concussion/frag(src) - new /obj/item/weapon/grenade/syndieminibomb/concussion/frag(src) - new /obj/item/weapon/grenade/syndieminibomb/concussion/frag(src) - new /obj/item/weapon/grenade/gluon(src) - new /obj/item/weapon/grenade/gluon(src) - new /obj/item/weapon/grenade/gluon(src) - new /obj/item/weapon/grenade/gluon(src) - new /obj/item/weapon/grenade/chem_grenade/incendiary(src) - new /obj/item/weapon/grenade/chem_grenade/incendiary(src) - new /obj/item/weapon/grenade/chem_grenade/facid(src) - new /obj/item/weapon/grenade/syndieminibomb(src) - new /obj/item/weapon/grenade/syndieminibomb(src) - new /obj/item/weapon/screwdriver(src) - new /obj/item/device/multitool(src) - -/obj/item/weapon/storage/belt/wands - name = "wand belt" - desc = "A belt designed to hold various rods of power. A veritable fanny pack of exotic magic." - icon_state = "soulstonebelt" - item_state = "soulstonebelt" - storage_slots = 6 - can_hold = list( - /obj/item/weapon/gun/magic/wand - ) - -/obj/item/weapon/storage/belt/wands/full/PopulateContents() - new /obj/item/weapon/gun/magic/wand/death(src) - new /obj/item/weapon/gun/magic/wand/resurrection(src) - new /obj/item/weapon/gun/magic/wand/polymorph(src) - new /obj/item/weapon/gun/magic/wand/teleport(src) - new /obj/item/weapon/gun/magic/wand/door(src) - new /obj/item/weapon/gun/magic/wand/fireball(src) - - for(var/obj/item/weapon/gun/magic/wand/W in contents) //All wands in this pack come in the best possible condition - W.max_charges = initial(W.max_charges) - W.charges = W.max_charges - -/obj/item/weapon/storage/belt/janitor - name = "janibelt" - desc = "A belt used to hold most janitorial supplies." - icon_state = "janibelt" - item_state = "janibelt" - storage_slots = 6 - max_w_class = WEIGHT_CLASS_BULKY // Set to this so the light replacer can fit. - can_hold = list( - /obj/item/weapon/grenade/chem_grenade, - /obj/item/device/lightreplacer, - /obj/item/device/flashlight, - /obj/item/weapon/reagent_containers/spray, - /obj/item/weapon/soap, - /obj/item/weapon/holosign_creator, - /obj/item/key/janitor, - /obj/item/clothing/gloves - ) - -/obj/item/weapon/storage/belt/bandolier - name = "bandolier" - desc = "A bandolier for holding shotgun ammunition." - icon_state = "bandolier" - item_state = "bandolier" - storage_slots = 18 - display_contents_with_number = TRUE - can_hold = list( - /obj/item/ammo_casing/shotgun - ) - -/obj/item/weapon/storage/belt/holster - name = "shoulder holster" - desc = "A holster to carry a handgun and ammo. WARNING: Badasses only." - icon_state = "holster" - item_state = "holster" - storage_slots = 3 - max_w_class = WEIGHT_CLASS_NORMAL - can_hold = list( - /obj/item/weapon/gun/ballistic/automatic/pistol, - /obj/item/weapon/gun/ballistic/revolver, - /obj/item/ammo_box, - ) - alternate_worn_layer = UNDER_SUIT_LAYER - -/obj/item/weapon/storage/belt/holster/full/PopulateContents() - new /obj/item/weapon/gun/ballistic/revolver/detective(src) - new /obj/item/ammo_box/c38(src) - new /obj/item/ammo_box/c38(src) - -/obj/item/weapon/storage/belt/fannypack - name = "fannypack" - desc = "A dorky fannypack for keeping small items in." - icon_state = "fannypack_leather" - item_state = "fannypack_leather" - storage_slots = 3 - max_w_class = WEIGHT_CLASS_SMALL - -/obj/item/weapon/storage/belt/fannypack/black - name = "black fannypack" - icon_state = "fannypack_black" - item_state = "fannypack_black" - -/obj/item/weapon/storage/belt/fannypack/red - name = "red fannypack" - icon_state = "fannypack_red" - item_state = "fannypack_red" - -/obj/item/weapon/storage/belt/fannypack/purple - name = "purple fannypack" - icon_state = "fannypack_purple" - item_state = "fannypack_purple" - -/obj/item/weapon/storage/belt/fannypack/blue - name = "blue fannypack" - icon_state = "fannypack_blue" - item_state = "fannypack_blue" - -/obj/item/weapon/storage/belt/fannypack/orange - name = "orange fannypack" - icon_state = "fannypack_orange" - item_state = "fannypack_orange" - -/obj/item/weapon/storage/belt/fannypack/white - name = "white fannypack" - icon_state = "fannypack_white" - item_state = "fannypack_white" - -/obj/item/weapon/storage/belt/fannypack/green - name = "green fannypack" - icon_state = "fannypack_green" - item_state = "fannypack_green" - -/obj/item/weapon/storage/belt/fannypack/pink - name = "pink fannypack" - icon_state = "fannypack_pink" - item_state = "fannypack_pink" - -/obj/item/weapon/storage/belt/fannypack/cyan - name = "cyan fannypack" - icon_state = "fannypack_cyan" - item_state = "fannypack_cyan" - -/obj/item/weapon/storage/belt/fannypack/yellow - name = "yellow fannypack" - icon_state = "fannypack_yellow" - item_state = "fannypack_yellow" - -/obj/item/weapon/storage/belt/sabre - name = "sabre sheath" - desc = "An ornate sheath designed to hold an officer's blade." - icon_state = "sheath" - item_state = "sheath" - storage_slots = 1 - rustle_jimmies = FALSE - w_class = WEIGHT_CLASS_BULKY - max_w_class = WEIGHT_CLASS_BULKY - can_hold = list( - /obj/item/weapon/melee/sabre - ) - -/obj/item/weapon/storage/belt/sabre/examine(mob/user) - ..() - if(contents.len) - to_chat(user, "Alt-click it to quickly draw the blade.") - -/obj/item/weapon/storage/belt/sabre/AltClick(mob/user) - if(!ishuman(user) || !user.canUseTopic(src, be_close=TRUE)) - return - if(contents.len) - var/obj/item/I = contents[1] - user.visible_message("[user] takes [I] out of [src].", "You take [I] out of [src].",\ - ) - user.put_in_hands(I) - update_icon() - else - to_chat(user, "[src] is empty.") - -/obj/item/weapon/storage/belt/sabre/update_icon() - icon_state = "sheath" - item_state = "sheath" - if(contents.len) - icon_state += "-sabre" - item_state += "-sabre" - if(loc && isliving(loc)) - var/mob/living/L = loc - L.regenerate_icons() - ..() - - -/obj/item/weapon/storage/belt/sabre/PopulateContents() - new /obj/item/weapon/melee/sabre(src) - update_icon() diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index f2537945db..fb5a4344f0 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -13,6 +13,7 @@ var/integrity_failure = 0 //0 if we have no special broken behavior var/resistance_flags = 0 // INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ON_FIRE | UNACIDABLE | ACID_PROOF + var/can_be_hit = TRUE //can this be bludgeoned by items? var/acid_level = 0 //how much acid is on that obj diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm index ad1ba46d86..ac8349f5f7 100644 --- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm +++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm @@ -119,9 +119,6 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) /datum/gas_mixture/proc/return_volume() //liters return max(0, volume) -/datum/gas_mixture/proc/thermal_energy() //joules - return temperature * heat_capacity() - /datum/gas_mixture/proc/archive() //Update archived versions of variables //Returns: 1 in all cases @@ -424,7 +421,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache()) var/list/cached_gases = gases var/temp = temperature - var/ener = thermal_energy() + var/ener = THERMAL_ENERGY(src) reaction_loop: for(var/r in SSair.gas_reactions) diff --git a/code/modules/atmospherics/gasmixtures/reactions.dm b/code/modules/atmospherics/gasmixtures/reactions.dm index a37e4d61c4..abbfcf8924 100644 --- a/code/modules/atmospherics/gasmixtures/reactions.dm +++ b/code/modules/atmospherics/gasmixtures/reactions.dm @@ -199,13 +199,13 @@ var/old_heat_capacity = air.heat_capacity() var/carbon_efficency = min(cached_gases["plasma"][MOLES]/cached_gases["co2"][MOLES],MAX_CARBON_EFFICENCY) - var/reaction_energy = air.thermal_energy() + var/reaction_energy = THERMAL_ENERGY(air) var/moles_impurities = air.total_moles()-(cached_gases["plasma"][MOLES]+cached_gases["co2"][MOLES]) var/plasma_fused = (PLASMA_FUSED_COEFFICENT*carbon_efficency)*(temperature/PLASMA_BINDING_ENERGY) var/carbon_catalyzed = (CARBON_CATALYST_COEFFICENT*carbon_efficency)*(temperature/PLASMA_BINDING_ENERGY) var/oxygen_added = carbon_catalyzed - var/nitrogen_added = (plasma_fused-oxygen_added)-(air.thermal_energy()/PLASMA_BINDING_ENERGY) + var/nitrogen_added = (plasma_fused-oxygen_added)-(THERMAL_ENERGY(air)/PLASMA_BINDING_ENERGY) reaction_energy = max(reaction_energy+((carbon_efficency*cached_gases["plasma"][MOLES])/((moles_impurities/carbon_efficency)+2)*10)+((plasma_fused/(moles_impurities/carbon_efficency))*PLASMA_BINDING_ENERGY),0) diff --git a/code/modules/atmospherics/machinery/datum_pipeline.dm b/code/modules/atmospherics/machinery/datum_pipeline.dm index 11a7881a98..ad7fdb921a 100644 --- a/code/modules/atmospherics/machinery/datum_pipeline.dm +++ b/code/modules/atmospherics/machinery/datum_pipeline.dm @@ -232,7 +232,7 @@ total_gas_mixture.merge(G) - total_thermal_energy += G.thermal_energy() + total_thermal_energy += THERMAL_ENERGY(G) total_heat_capacity += G.heat_capacity() total_gas_mixture.temperature = total_heat_capacity ? total_thermal_energy/total_heat_capacity : 0 diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index d89671d06d..da4fe1cf5c 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -17,13 +17,25 @@ dog_fashion = /datum/dog_fashion/head/helmet - -/obj/item/clothing/head/helmet/Initialize() - . = ..() - /obj/item/clothing/head/helmet/sec can_flashlight = 1 +/obj/item/clothing/head/helmet/sec/attackby(obj/item/I, mob/user, params) + if(issignaler(I)) + var/obj/item/device/assembly/signaler/S = I + if(F) //Has a flashlight. Player must remove it, else it will be lost forever. + to_chat(user, "The mounted flashlight is in the way, remove it first!") + return + + if(S.secured) + qdel(S) + var/obj/item/secbot_assembly/A = new /obj/item/secbot_assembly + user.put_in_hands(A) + to_chat(user, "You add the signaler to the helmet.") + qdel(src) + return + return ..() + /obj/item/clothing/head/helmet/alt name = "bulletproof helmet" desc = "A bulletproof combat helmet that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent." @@ -281,7 +293,7 @@ qdel(THL) return - ..() + return ..() /obj/item/clothing/head/helmet/proc/toggle_helmlight() set name = "Toggle Helmetlight" diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm index 314f39f48b..4a86008ad2 100644 --- a/code/modules/clothing/head/misc_special.dm +++ b/code/modules/clothing/head/misc_special.dm @@ -1,179 +1,221 @@ -/* - * Contents: - * Welding mask - * Cakehat - * Ushanka - * Pumpkin head - * Kitty ears - * Cardborg disguise - */ - -/* - * Welding mask - */ -/obj/item/clothing/head/welding - name = "welding helmet" - desc = "A head-mounted face cover designed to protect the wearer completely from space-arc eye." - icon_state = "welding" - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - item_state = "welding" - materials = list(MAT_METAL=1750, MAT_GLASS=400) -// var/up = 0 - flash_protect = 2 - tint = 2 - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 60) - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - actions_types = list(/datum/action/item_action/toggle) - visor_flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - visor_flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - resistance_flags = FIRE_PROOF - -/obj/item/clothing/head/welding/attack_self(mob/user) - weldingvisortoggle(user) - - -/* - * Cakehat - */ -/obj/item/clothing/head/hardhat/cakehat - name = "cakehat" - desc = "You put the cake on your head. Brilliant." - icon_state = "hardhat0_cakehat" - item_state = "hardhat0_cakehat" - item_color = "cakehat" - hitsound = 'sound/weapons/tap.ogg' - flags_inv = HIDEEARS|HIDEHAIR - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) - brightness_on = 2 //luminosity when on - flags_cover = HEADCOVERSEYES - heat = 100 - -/obj/item/clothing/head/hardhat/cakehat/process() - var/turf/location = src.loc - if(ishuman(location)) - var/mob/living/carbon/human/M = location - if(M.is_holding(src) || M.head == src) - location = M.loc - - if(isturf(location)) - location.hotspot_expose(700, 1) - -/obj/item/clothing/head/hardhat/cakehat/turn_on() - ..() - force = 2 - throwforce = 2 - damtype = BURN - hitsound = 'sound/items/welder.ogg' - START_PROCESSING(SSobj, src) - -/obj/item/clothing/head/hardhat/cakehat/turn_off() - ..() - force = 0 - throwforce = 0 - damtype = BRUTE - hitsound = 'sound/weapons/tap.ogg' - STOP_PROCESSING(SSobj, src) - -/obj/item/clothing/head/hardhat/cakehat/is_hot() - return on * heat -/* - * Ushanka - */ -/obj/item/clothing/head/ushanka - name = "ushanka" - desc = "Perfect for winter in Siberia, da?" - icon_state = "ushankadown" - item_state = "ushankadown" - flags_inv = HIDEEARS|HIDEHAIR - var/earflaps = 1 - cold_protection = HEAD - min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT - - dog_fashion = /datum/dog_fashion/head/ushanka - -/obj/item/clothing/head/ushanka/attack_self(mob/user) - if(earflaps) - src.icon_state = "ushankaup" - src.item_state = "ushankaup" - earflaps = 0 - to_chat(user, "You raise the ear flaps on the ushanka.") - else - src.icon_state = "ushankadown" - src.item_state = "ushankadown" - earflaps = 1 - to_chat(user, "You lower the ear flaps on the ushanka.") - -/* - * Pumpkin head - */ -/obj/item/clothing/head/hardhat/pumpkinhead - name = "carved pumpkin" - desc = "A jack o' lantern! Believed to ward off evil spirits." - icon_state = "hardhat0_pumpkin" - item_state = "hardhat0_pumpkin" - item_color = "pumpkin" - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) - brightness_on = 2 //luminosity when on - flags_cover = HEADCOVERSEYES - -/* - * Kitty ears - */ -/obj/item/clothing/head/kitty - name = "kitty ears" - desc = "A pair of kitty ears. Meow!" - icon_state = "kitty" - color = "#999999" - dynamic_hair_suffix = "" - - dog_fashion = /datum/dog_fashion/head/kitty - -/obj/item/clothing/head/kitty/equipped(mob/living/carbon/human/user, slot) - if(ishuman(user) && slot == slot_head) - update_icon(user) - user.update_inv_head() //Color might have been changed by update_icon. - ..() - -/obj/item/clothing/head/kitty/update_icon(mob/living/carbon/human/user) - if(ishuman(user)) - add_atom_colour("#[user.hair_color]", FIXED_COLOUR_PRIORITY) - -/obj/item/clothing/head/kitty/genuine - desc = "A pair of kitty ears. A tag on the inside says \"Hand made from real cats.\"" - - -/obj/item/clothing/head/hardhat/reindeer - name = "novelty reindeer hat" - desc = "Some fake antlers and a very fake red nose." - icon_state = "hardhat0_reindeer" - item_state = "hardhat0_reindeer" - item_color = "reindeer" - flags_inv = 0 - armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) - brightness_on = 1 //luminosity when on - dynamic_hair_suffix = "" - - dog_fashion = /datum/dog_fashion/head/reindeer - -/obj/item/clothing/head/cardborg - name = "cardborg helmet" - desc = "A helmet made out of a box." - icon_state = "cardborg_h" - item_state = "cardborg_h" - flags_cover = HEADCOVERSEYES - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR - - dog_fashion = /datum/dog_fashion/head/cardborg - -/obj/item/clothing/head/cardborg/equipped(mob/living/user, slot) - ..() - if(ishuman(user) && slot == slot_head) - var/mob/living/carbon/human/H = user - if(istype(H.wear_suit, /obj/item/clothing/suit/cardborg)) - var/obj/item/clothing/suit/cardborg/CB = H.wear_suit - CB.disguise(user, src) - -/obj/item/clothing/head/cardborg/dropped(mob/living/user) - ..() - user.remove_alt_appearance("standard_borg_disguise") +/* + * Contents: + * Welding mask + * Cakehat + * Ushanka + * Pumpkin head + * Kitty ears + * Cardborg disguise + */ + +/* + * Welding mask + */ +/obj/item/clothing/head/welding + name = "welding helmet" + desc = "A head-mounted face cover designed to protect the wearer completely from space-arc eye." + icon_state = "welding" + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + item_state = "welding" + materials = list(MAT_METAL=1750, MAT_GLASS=400) +// var/up = 0 + flash_protect = 2 + tint = 2 + armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 60) + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + actions_types = list(/datum/action/item_action/toggle) + visor_flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + visor_flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + resistance_flags = FIRE_PROOF + +/obj/item/clothing/head/welding/attack_self(mob/user) + weldingvisortoggle(user) + + +/* + * Cakehat + */ +/obj/item/clothing/head/hardhat/cakehat + name = "cakehat" + desc = "You put the cake on your head. Brilliant." + icon_state = "hardhat0_cakehat" + item_state = "hardhat0_cakehat" + item_color = "cakehat" + hitsound = 'sound/weapons/tap.ogg' + flags_inv = HIDEEARS|HIDEHAIR + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) + brightness_on = 2 //luminosity when on + flags_cover = HEADCOVERSEYES + heat = 1000 + +/obj/item/clothing/head/hardhat/cakehat/process() + var/turf/location = src.loc + if(ishuman(location)) + var/mob/living/carbon/human/M = location + if(M.is_holding(src) || M.head == src) + location = M.loc + + if(isturf(location)) + location.hotspot_expose(700, 1) + +/obj/item/clothing/head/hardhat/cakehat/turn_on() + ..() + force = 15 + throwforce = 15 + damtype = BURN + hitsound = 'sound/items/welder.ogg' + START_PROCESSING(SSobj, src) + +/obj/item/clothing/head/hardhat/cakehat/turn_off() + ..() + force = 0 + throwforce = 0 + damtype = BRUTE + hitsound = 'sound/weapons/tap.ogg' + STOP_PROCESSING(SSobj, src) + +/obj/item/clothing/head/hardhat/cakehat/is_hot() + return on * heat +/* + * Ushanka + */ +/obj/item/clothing/head/ushanka + name = "ushanka" + desc = "Perfect for winter in Siberia, da?" + icon_state = "ushankadown" + item_state = "ushankadown" + flags_inv = HIDEEARS|HIDEHAIR + var/earflaps = 1 + cold_protection = HEAD + min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT + + dog_fashion = /datum/dog_fashion/head/ushanka + +/obj/item/clothing/head/ushanka/attack_self(mob/user) + if(earflaps) + src.icon_state = "ushankaup" + src.item_state = "ushankaup" + earflaps = 0 + to_chat(user, "You raise the ear flaps on the ushanka.") + else + src.icon_state = "ushankadown" + src.item_state = "ushankadown" + earflaps = 1 + to_chat(user, "You lower the ear flaps on the ushanka.") + +/* + * Pumpkin head + */ +/obj/item/clothing/head/hardhat/pumpkinhead + name = "carved pumpkin" + desc = "A jack o' lantern! Believed to ward off evil spirits." + icon_state = "hardhat0_pumpkin" + item_state = "hardhat0_pumpkin" + item_color = "pumpkin" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) + brightness_on = 2 //luminosity when on + flags_cover = HEADCOVERSEYES + +/* + * Kitty ears + */ +/obj/item/clothing/head/kitty + name = "kitty ears" + desc = "A pair of kitty ears. Meow!" + icon_state = "kitty" + color = "#999999" + dynamic_hair_suffix = "" + + dog_fashion = /datum/dog_fashion/head/kitty + +/obj/item/clothing/head/kitty/equipped(mob/living/carbon/human/user, slot) + if(ishuman(user) && slot == slot_head) + update_icon(user) + user.update_inv_head() //Color might have been changed by update_icon. + ..() + +/obj/item/clothing/head/kitty/update_icon(mob/living/carbon/human/user) + if(ishuman(user)) + add_atom_colour("#[user.hair_color]", FIXED_COLOUR_PRIORITY) + +/obj/item/clothing/head/kitty/genuine + desc = "A pair of kitty ears. A tag on the inside says \"Hand made from real cats.\"" + + +/obj/item/clothing/head/hardhat/reindeer + name = "novelty reindeer hat" + desc = "Some fake antlers and a very fake red nose." + icon_state = "hardhat0_reindeer" + item_state = "hardhat0_reindeer" + item_color = "reindeer" + flags_inv = 0 + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) + brightness_on = 1 //luminosity when on + dynamic_hair_suffix = "" + + dog_fashion = /datum/dog_fashion/head/reindeer + +/obj/item/clothing/head/cardborg + name = "cardborg helmet" + desc = "A helmet made out of a box." + icon_state = "cardborg_h" + item_state = "cardborg_h" + flags_cover = HEADCOVERSEYES + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR + + dog_fashion = /datum/dog_fashion/head/cardborg + +/obj/item/clothing/head/cardborg/equipped(mob/living/user, slot) + ..() + if(ishuman(user) && slot == slot_head) + var/mob/living/carbon/human/H = user + if(istype(H.wear_suit, /obj/item/clothing/suit/cardborg)) + var/obj/item/clothing/suit/cardborg/CB = H.wear_suit + CB.disguise(user, src) + +/obj/item/clothing/head/cardborg/dropped(mob/living/user) + ..() + user.remove_alt_appearance("standard_borg_disguise") + + + +/obj/item/clothing/head/wig + name = "wig" + desc = "A bunch of hair without a head attached." + icon_state = "" + item_state = "pwig" + flags_inv = HIDEHAIR + var/hair_style = "Very Long Hair" + var/hair_color = "#000" + +/obj/item/clothing/head/wig/Initialize(mapload) + . = ..() + update_icon() + +/obj/item/clothing/head/wig/update_icon() + cut_overlays() + var/datum/sprite_accessory/S = GLOB.hair_styles_list[hair_style] + if(!S) + icon_state = "pwig" + else + var/mutable_appearance/M = mutable_appearance(S.icon,S.icon_state) + M.appearance_flags |= RESET_COLOR + M.color = hair_color + add_overlay(M) + +/obj/item/clothing/head/wig/worn_overlays(isinhands = FALSE, file2use) + . = list() + if(!isinhands) + var/datum/sprite_accessory/S = GLOB.hair_styles_list[hair_style] + if(!S) + return + var/mutable_appearance/M = mutable_appearance(S.icon, S.icon_state,layer = -HAIR_LAYER) + M.appearance_flags |= RESET_COLOR + M.color = hair_color + . += M + +/obj/item/clothing/head/wig/random/Initialize(mapload) + hair_style = pick(GLOB.hair_styles_list - "Bald") //Don't want invisible wig + hair_color = "#[random_short_color()]" + . = ..() diff --git a/code/modules/mining/equipment/explorer_gear.dm b/code/modules/mining/equipment/explorer_gear.dm index 1b21b88447..f2cdefa9a4 100644 --- a/code/modules/mining/equipment/explorer_gear.dm +++ b/code/modules/mining/equipment/explorer_gear.dm @@ -46,3 +46,64 @@ /obj/item/clothing/mask/gas/explorer/folded/New() ..() adjustmask() + +/obj/item/clothing/suit/space/hostile_environment + name = "H.E.C.K. suit" + desc = "Hostile Environiment Cross-Kinetic Suit: A suit designed to withstand the wide variety of hazards from Lavaland. It wasn't enough for its last owner." + icon_state = "hostile_env" + item_state = "hostile_env" + flags_1 = THICKMATERIAL_1 //not spaceproof + max_heat_protection_temperature = FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | LAVA_PROOF + slowdown = 0 + armor = list("melee" = 70, "bullet" = 40, "laser" = 10, "energy" = 10, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + allowed = list(/obj/item/device/flashlight, /obj/item/tank/internals, /obj/item/resonator, /obj/item/device/mining_scanner, /obj/item/device/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe) + +/obj/item/clothing/suit/space/hostile_environment/Initialize() + . = ..() + AddComponent(/datum/component/spraycan_paintable) + START_PROCESSING(SSobj, src) + +/obj/item/clothing/suit/space/hostile_environment/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/clothing/suit/space/hostile_environment/process() + var/mob/living/carbon/C = loc + if(istype(C) && prob(2)) //cursed by bubblegum + if(prob(15)) + new /datum/hallucination/oh_yeah(C) + to_chat(C, "[pick("I AM IMMORTAL.","I SHALL TAKE BACK WHAT'S MINE.","I SEE YOU.","YOU CANNOT ESCAPE ME FOREVER.","DEATH CANNOT HOLD ME.")]") + else + to_chat(C, "[pick("You hear faint whispers.","You smell ash.","You feel hot.","You hear a roar in the distance.")]") + +/obj/item/clothing/head/helmet/space/hostile_environment + name = "H.E.C.K. helmet" + desc = "Hostile Environiment Cross-Kinetic Helmet: A helmet designed to withstand the wide variety of hazards from Lavaland. It wasn't enough for its last owner." + icon_state = "hostile_env" + item_state = "hostile_env" + w_class = WEIGHT_CLASS_NORMAL + max_heat_protection_temperature = FIRE_IMMUNITY_HELM_MAX_TEMP_PROTECT + flags_1 = THICKMATERIAL_1 // no space protection + armor = list("melee" = 70, "bullet" = 40, "laser" = 10, "energy" = 10, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF | LAVA_PROOF + +/obj/item/clothing/head/helmet/space/hostile_environment/Initialize() + . = ..() + AddComponent(/datum/component/spraycan_paintable) + update_icon() + +/obj/item/clothing/head/helmet/space/hostile_environment/update_icon() + ..() + cut_overlays() + var/mutable_appearance/glass_overlay = mutable_appearance(icon, "hostile_env_glass") + glass_overlay.appearance_flags = RESET_COLOR + add_overlay(glass_overlay) + +/obj/item/clothing/head/helmet/space/hostile_environment/worn_overlays(isinhands) + . = ..() + if(!isinhands) + var/mutable_appearance/M = mutable_appearance('icons/mob/head.dmi', "hostile_env_glass") + M.appearance_flags = RESET_COLOR + . += M + diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index e16077fec1..95c4898605 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -932,6 +932,8 @@ name = "bubblegum chest" /obj/structure/closet/crate/necropolis/bubblegum/PopulateContents() + new /obj/item/clothing/suit/space/hostile_environment(src) + new /obj/item/clothing/head/helmet/space/hostile_environment(src) var/loot = rand(1,3) switch(loot) if(1) diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 666fd767ba..8a978bc44b 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -79,9 +79,12 @@ if(SSticker.current_state == GAME_STATE_PREGAME) var/time_remaining = SSticker.GetTimeLeft() - if(time_remaining >= 0) - time_remaining /= 10 - stat("Time To Start:", (time_remaining >= 0) ? "[round(time_remaining)]s" : "DELAYED") + if(time_remaining > 0) + stat("Time To Start:", "[round(time_remaining/10)]s") + else if(time_remaining == -10) + stat("Time To Start:", "DELAYED") + else + stat("Time To Start:", "SOON") stat("Players:", "[SSticker.totalPlayers]") if(client.holder) diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index 9f31c5b055..bbe02a3f8e 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -29,6 +29,7 @@ key_third_person = "bows" message = "bows." message_param = "bows to %t." + restraint_check = TRUE /datum/emote/living/burp key = "burp" @@ -119,6 +120,7 @@ key = "flap" key_third_person = "flaps" message = "flaps their wings." + restraint_check = TRUE var/wing_time = 20 /datum/emote/living/flap/run_emote(mob/user, params) @@ -138,6 +140,7 @@ key = "aflap" key_third_person = "aflaps" message = "flaps their wings ANGRILY!" + restraint_check = TRUE wing_time = 10 /datum/emote/living/flip @@ -147,7 +150,7 @@ /datum/emote/living/flip/run_emote(mob/user, params) . = ..() - if(!.) + if(.) user.SpinAnimation(7,1) /datum/emote/living/frown @@ -454,7 +457,7 @@ if(e in keys) continue E = emote_list[e] - if(E.can_run_emote(user, TRUE)) + if(E.can_run_emote(user, status_check = FALSE)) keys += E.key keys = sortList(keys) @@ -481,18 +484,21 @@ /datum/emote/living/spin key = "spin" key_third_person = "spins" + restraint_check = TRUE /datum/emote/living/spin/run_emote(mob/user) - user.spin(20, 1) - if(iscyborg(user)) - var/mob/living/silicon/robot/R = user - if(R.buckled_mobs) - for(var/mob/M in R.buckled_mobs) - if(R.riding_datum) - R.riding_datum.force_dismount(M) - else - R.unbuckle_all_mobs() - ..() + . = ..() + if(.) + user.spin(20, 1) + if(iscyborg(user)) + var/mob/living/silicon/robot/R = user + if(R.buckled_mobs) + for(var/mob/M in R.buckled_mobs) + if(R.riding_datum) + R.riding_datum.force_dismount(M) + else + R.unbuckle_all_mobs() + /datum/emote/living/circle key = "circle" diff --git a/code/modules/mob/living/simple_animal/bot/construction.dm b/code/modules/mob/living/simple_animal/bot/construction.dm index 0d1f346dcf..c53925e076 100644 --- a/code/modules/mob/living/simple_animal/bot/construction.dm +++ b/code/modules/mob/living/simple_animal/bot/construction.dm @@ -357,28 +357,6 @@ var/build_step = 0 var/created_name = "Securitron" //To preserve the name if it's a unique securitron I guess -/obj/item/clothing/head/helmet/attackby(obj/item/device/assembly/signaler/S, mob/user, params) - ..() - if(!issignaler(S)) - ..() - return - - if(type != /obj/item/clothing/head/helmet/sec) //Eh, but we don't want people making secbots out of space helmets. - return - - if(F) //Has a flashlight. Player must remove it, else it will be lost forever. - to_chat(user, "The mounted flashlight is in the way, remove it first!") - return - - if(S.secured) - qdel(S) - var/obj/item/secbot_assembly/A = new /obj/item/secbot_assembly - user.put_in_hands(A) - to_chat(user, "You add the signaler to the helmet.") - qdel(src) - else - return - /obj/item/secbot_assembly/attackby(obj/item/I, mob/user, params) ..() if(istype(I, /obj/item/weldingtool)) diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index 8395ef4d83..cf38d5bfff 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -8,7 +8,7 @@ hitsound_wall = "ricochet" impact_effect_type = /obj/effect/temp_visual/impact_effect -/obj/item/projectile/bullet/pellet/Range() +/obj/item/projectile/bullet/pellet/shotgun_buckshot/Range() ..() damage -= 0.75 if(damage < 0) diff --git a/html/changelogs/AutoChangeLog-pr-3157.yml b/html/changelogs/AutoChangeLog-pr-3157.yml new file mode 100644 index 0000000000..69e7b10920 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3157.yml @@ -0,0 +1,4 @@ +author: "CitadelStationBot" +delete-after: True +changes: + - rscadd: "Wigs are now available in AutoDrobe" diff --git a/html/changelogs/AutoChangeLog-pr-3159.yml b/html/changelogs/AutoChangeLog-pr-3159.yml new file mode 100644 index 0000000000..d835ae4122 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3159.yml @@ -0,0 +1,7 @@ +author: "XDTM" +delete-after: True +changes: + - rscadd: "Added the H.E.C.K. suit, a guaranteed loot frop from Bubblegum." + - rscadd: "The H.E.C.K. suit is fully fire (and ash) proof, and has very good melee armor." + - rscadd: "H.E.C.K. suits can also be painted with spraycans, to fully customize your experience." + - rscadd: "Despite spending centuries inside a demon king, H.E.C.K. suits are most definitely safe." diff --git a/html/changelogs/AutoChangeLog-pr-3160.yml b/html/changelogs/AutoChangeLog-pr-3160.yml new file mode 100644 index 0000000000..c9d48dc583 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3160.yml @@ -0,0 +1,5 @@ +author: "Kevak/Neri" +delete-after: True +changes: + - bugfix: "Fixed plasmaman organsprites being missing" + - imageadd: "Plasmaman organ sprites added. Credit to Toriate for the sprites" diff --git a/html/changelogs/AutoChangeLog-pr-3163.yml b/html/changelogs/AutoChangeLog-pr-3163.yml new file mode 100644 index 0000000000..3c82b42146 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3163.yml @@ -0,0 +1,4 @@ +author: "Robustin" +delete-after: True +changes: + - bugfix: "Emotes (e.g. spinning and flipping) will now properly check for consciousness, restraints, etc. when appropriate." diff --git a/html/changelogs/AutoChangeLog-pr-3168.yml b/html/changelogs/AutoChangeLog-pr-3168.yml new file mode 100644 index 0000000000..ee61a58860 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3168.yml @@ -0,0 +1,4 @@ +author: "Naksu" +delete-after: True +changes: + - tweak: "Made atmos tiny bit faster" diff --git a/html/changelogs/AutoChangeLog-pr-3170.yml b/html/changelogs/AutoChangeLog-pr-3170.yml new file mode 100644 index 0000000000..eeb9a06f55 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3170.yml @@ -0,0 +1,6 @@ +author: "Joan" +delete-after: True +changes: + - balance: "Ocular Warden base damage per second changed from 12.5 to 15." + - bugfix: "Ocular Wardens no longer count themselves when checking for dense objects, which decreased their overall damage by 15%." + - balance: "Ocular Wardens now only reduce their damage by 10% per dense object, and only do so once per turf. However, dense turfs now reduce their damage with the same 10% penalty." diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi index d18a1bf262..18bbcb3610 100755 Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 8b927cf2bb..b996531842 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -294,6 +294,7 @@ #include "code\datums\components\_component.dm" #include "code\datums\components\archaeology.dm" #include "code\datums\components\material_container.dm" +#include "code\datums\components\paintable.dm" #include "code\datums\components\slippery.dm" #include "code\datums\components\squeek.dm" #include "code\datums\diseases\_disease.dm"