diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql index e0f33226e8c6..19521d796864 100644 --- a/SQL/paradise_schema.sql +++ b/SQL/paradise_schema.sql @@ -254,7 +254,7 @@ CREATE TABLE `player` ( `UI_style_alpha` smallint(4) DEFAULT '255', `be_role` mediumtext, `default_slot` smallint(4) DEFAULT '1', - `toggles` mediumint(8) DEFAULT '383', + `toggles` int(8) DEFAULT '383', `sound` mediumint(8) DEFAULT '31', `randomslot` tinyint(1) DEFAULT '0', `volume` smallint(4) DEFAULT '100', diff --git a/SQL/paradise_schema_prefixed.sql b/SQL/paradise_schema_prefixed.sql index ecb28a867e47..ff4026c7d00b 100644 --- a/SQL/paradise_schema_prefixed.sql +++ b/SQL/paradise_schema_prefixed.sql @@ -253,7 +253,7 @@ CREATE TABLE `SS13_player` ( `UI_style_alpha` smallint(4) DEFAULT '255', `be_role` mediumtext, `default_slot` smallint(4) DEFAULT '1', - `toggles` mediumint(8) DEFAULT '383', + `toggles` int(8) DEFAULT '383', `sound` mediumint(8) DEFAULT '31', `randomslot` tinyint(1) DEFAULT '0', `volume` smallint(4) DEFAULT '100', diff --git a/SQL/updates/6-7.sql b/SQL/updates/6-7.sql new file mode 100644 index 000000000000..c871302b2dec --- /dev/null +++ b/SQL/updates/6-7.sql @@ -0,0 +1,2 @@ +ALTER TABLE player +MODIFY COLUMN toggles int; \ No newline at end of file diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm index a6b346ee1c20..d81ea35e1e9e 100644 --- a/code/ATMOSPHERICS/atmospherics.dm +++ b/code/ATMOSPHERICS/atmospherics.dm @@ -248,7 +248,8 @@ GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new()) // Ventcrawling #define VENT_SOUND_DELAY 30 /obj/machinery/atmospherics/relaymove(mob/living/user, direction) - if(!(direction & initialize_directions)) //can't go in a way we aren't connecting to + direction &= initialize_directions + if(!direction || !(direction in cardinal)) //cant go this way. return if(buckled_mob == user) diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm new file mode 100644 index 000000000000..e28708a81e61 --- /dev/null +++ b/code/__DEFINES/colors.dm @@ -0,0 +1,54 @@ + +#define COLOR_INPUT_DISABLED "#F0F0F0" +#define COLOR_INPUT_ENABLED "#D3B5B5" +#define COLOR_WHITE "#EEEEEE" +#define COLOR_SILVER "#C0C0C0" +#define COLOR_GRAY "#808080" +#define COLOR_FLOORTILE_GRAY "#8D8B8B" +#define COLOR_ALMOST_BLACK "#333333" +#define COLOR_BLACK "#000000" +#define COLOR_RED "#FF0000" +#define COLOR_RED_LIGHT "#FF3333" +#define COLOR_MAROON "#800000" +#define COLOR_YELLOW "#FFFF00" +#define COLOR_OLIVE "#808000" +#define COLOR_LIME "#32CD32" +#define COLOR_GREEN "#008000" +#define COLOR_CYAN "#00FFFF" +#define COLOR_TEAL "#008080" +#define COLOR_BLUE "#0000FF" +#define COLOR_BLUE_LIGHT "#33CCFF" +#define COLOR_NAVY "#000080" +#define COLOR_PINK "#FFC0CB" +#define COLOR_MAGENTA "#FF00FF" +#define COLOR_PURPLE "#800080" +#define COLOR_ORANGE "#FF9900" +#define COLOR_BEIGE "#CEB689" +#define COLOR_BLUE_GRAY "#75A2BB" +#define COLOR_BROWN "#BA9F6D" +#define COLOR_DARK_BROWN "#997C4F" +#define COLOR_DARK_ORANGE "#C3630C" +#define COLOR_GREEN_GRAY "#99BB76" +#define COLOR_RED_GRAY "#B4696A" +#define COLOR_PALE_BLUE_GRAY "#98C5DF" +#define COLOR_PALE_GREEN_GRAY "#B7D993" +#define COLOR_PALE_RED_GRAY "#D59998" +#define COLOR_PALE_PURPLE_GRAY "#CBB1CA" +#define COLOR_PURPLE_GRAY "#AE8CA8" + +//Color defines used by the assembly detailer. +#define COLOR_ASSEMBLY_BLACK "#545454" +#define COLOR_ASSEMBLY_BGRAY "#9497AB" +#define COLOR_ASSEMBLY_WHITE "#E2E2E2" +#define COLOR_ASSEMBLY_RED "#CC4242" +#define COLOR_ASSEMBLY_ORANGE "#E39751" +#define COLOR_ASSEMBLY_BEIGE "#AF9366" +#define COLOR_ASSEMBLY_BROWN "#97670E" +#define COLOR_ASSEMBLY_GOLD "#AA9100" +#define COLOR_ASSEMBLY_YELLOW "#CECA2B" +#define COLOR_ASSEMBLY_GURKHA "#999875" +#define COLOR_ASSEMBLY_LGREEN "#789876" +#define COLOR_ASSEMBLY_GREEN "#44843C" +#define COLOR_ASSEMBLY_LBLUE "#5D99BE" +#define COLOR_ASSEMBLY_BLUE "#38559E" +#define COLOR_ASSEMBLY_PURPLE "#6F6192" diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index a8c6b4bd5e86..fbc28695ebf4 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -70,6 +70,10 @@ #define ATTACK_EFFECT_MECHFIRE "mech_fire" #define ATTACK_EFFECT_MECHTOXIN "mech_toxin" #define ATTACK_EFFECT_BOOP "boop" //Honk +//NOTE: INTENT_HOTKEY_* defines are not actual intents! +//they are here to support hotkeys +#define INTENT_HOTKEY_LEFT "left" +#define INTENT_HOTKEY_RIGHT "right" //Embedded objects #define EMBEDDED_PAIN_CHANCE 15 //Chance for embedded objects to cause pain (damage user) @@ -82,6 +86,22 @@ #define EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER 8 //Coefficient of multiplication for the damage the item does when removed without a surgery (this*item.w_class) #define EMBEDDED_UNSAFE_REMOVAL_TIME 30 //A Time in ticks, total removal time = (this*item.w_class) +// Body Parts +#define BODY_ZONE_HEAD "head" +#define BODY_ZONE_CHEST "chest" +#define BODY_ZONE_L_ARM "l_arm" +#define BODY_ZONE_R_ARM "r_arm" +#define BODY_ZONE_L_LEG "l_leg" +#define BODY_ZONE_R_LEG "r_leg" + +#define BODY_ZONE_PRECISE_EYES "eyes" +#define BODY_ZONE_PRECISE_MOUTH "mouth" +#define BODY_ZONE_PRECISE_GROIN "groin" +#define BODY_ZONE_PRECISE_L_HAND "l_hand" +#define BODY_ZONE_PRECISE_R_HAND "r_hand" +#define BODY_ZONE_PRECISE_L_FOOT "l_foot" +#define BODY_ZONE_PRECISE_R_FOOT "r_foot" + //Gun Stuff #define SAWN_INTACT 0 #define SAWN_OFF 1 diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index f82fc271fbd8..14b712754fb6 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -1,6 +1,9 @@ #define ALL ~0 //For convenience. #define NONE 0 +// for /datum/var/datum_flags +#define DF_USE_TAG (1<<0) + //FLAGS BITMASK #define STOPSPRESSUREDMAGE 1 //This flag is used on the flags variable for SUIT and HEAD items which stop pressure damage. Note that the flag 1 was previous used as ONBACK, so it is possible for some code to use (flags & 1) when checking if something can be put on your back. Replace this code with (inv_flags & SLOT_BACK) if you see it anywhere To successfully stop you taking all pressure damage you must have both a suit and head item with this flag. #define NODROP 2 // This flag makes it so that an item literally cannot be removed at all, or at least that's how it should be. Only deleted. diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 3418938d955d..9f2556df3a43 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -57,17 +57,6 @@ #define MAX_STACK_AMOUNT_GLASS 50 #define MAX_STACK_AMOUNT_RODS 60 -//some colors -#define COLOR_RED "#FF0000" -#define COLOR_GREEN "#00FF00" -#define COLOR_BLUE "#0000FF" -#define COLOR_CYAN "#00FFFF" -#define COLOR_PINK "#FF00FF" -#define COLOR_YELLOW "#FFFF00" -#define COLOR_ORANGE "#FF9900" -#define COLOR_WHITE "#FFFFFF" -#define COLOR_GRAY "#808080" - //FONTS: // Used by Paper and PhotoCopier (and PaperBin once a year). // Used by PDA's Notekeeper. @@ -326,7 +315,7 @@ #define INVESTIGATE_BOMB "bombs" // The SQL version required by this version of the code -#define SQL_VERSION 6 +#define SQL_VERSION 7 // Vending machine stuff #define CAT_NORMAL 1 diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index dbd29b1d4d15..9582fddccfe4 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -16,6 +16,7 @@ #define CHAT_GHOSTSIGHT 8 #define CHAT_PRAYER 16 #define CHAT_RADIO 32 +#define AZERTY 64 #define CHAT_DEBUGLOGS 128 #define CHAT_LOOC 256 #define CHAT_GHOSTRADIO 512 @@ -32,10 +33,11 @@ #define TYPING_ONCE 1048576 #define AMBIENT_OCCLUSION 2097152 #define CHAT_GHOSTPDA 4194304 +#define NUMPAD_TARGET 8388608 +#define TOGGLES_TOTAL 16777215 // If you add or remove a preference toggle above, make sure you update this define with the total value of the toggles combined. -#define TOGGLES_TOTAL 8388607 // If you add or remove a preference toggle above, make sure you update this define with the total value of the toggles combined. -#define TOGGLES_DEFAULT (CHAT_OOC|CHAT_DEAD|CHAT_GHOSTEARS|CHAT_GHOSTSIGHT|CHAT_PRAYER|CHAT_RADIO|CHAT_LOOC|MEMBER_PUBLIC|DONATOR_PUBLIC|AMBIENT_OCCLUSION|CHAT_GHOSTPDA) +#define TOGGLES_DEFAULT (CHAT_OOC|CHAT_DEAD|CHAT_GHOSTEARS|CHAT_GHOSTSIGHT|CHAT_PRAYER|CHAT_RADIO|CHAT_LOOC|MEMBER_PUBLIC|DONATOR_PUBLIC|AMBIENT_OCCLUSION|CHAT_GHOSTPDA|NUMPAD_TARGET) // Admin attack logs filter system, see /proc/add_attack_logs and /proc/msg_admin_attack #define ATKLOG_ALL 0 diff --git a/code/__HELPERS/pronouns.dm b/code/__HELPERS/pronouns.dm index 607ba3495d37..d419e9257599 100644 --- a/code/__HELPERS/pronouns.dm +++ b/code/__HELPERS/pronouns.dm @@ -43,7 +43,7 @@ . = "e[.]" //like clients, which do have gender. -/client/proc/p_they(capitalized, temp_gender) +/client/p_they(capitalized, temp_gender) if(!temp_gender) temp_gender = gender . = "they" @@ -55,7 +55,7 @@ if(capitalized) . = capitalize(.) -/client/proc/p_their(capitalized, temp_gender) +/client/p_their(capitalized, temp_gender) if(!temp_gender) temp_gender = gender . = "their" @@ -67,7 +67,7 @@ if(capitalized) . = capitalize(.) -/client/proc/p_them(capitalized, temp_gender) +/client/p_them(capitalized, temp_gender) if(!temp_gender) temp_gender = gender . = "them" @@ -79,35 +79,35 @@ if(capitalized) . = capitalize(.) -/client/proc/p_have(temp_gender) +/client/p_have(temp_gender) if(!temp_gender) temp_gender = gender . = "has" if(temp_gender == PLURAL || temp_gender == NEUTER) . = "have" -/client/proc/p_are(temp_gender) +/client/p_are(temp_gender) if(!temp_gender) temp_gender = gender . = "is" if(temp_gender == PLURAL || temp_gender == NEUTER) . = "are" -/client/proc/p_were(temp_gender) +/client/p_were(temp_gender) if(!temp_gender) temp_gender = gender . = "was" if(temp_gender == PLURAL || temp_gender == NEUTER) . = "were" -/client/proc/p_do(temp_gender) +/client/p_do(temp_gender) if(!temp_gender) temp_gender = gender . = "does" if(temp_gender == PLURAL || temp_gender == NEUTER) . = "do" -/client/proc/p_s(temp_gender) +/client/p_s(temp_gender) if(!temp_gender) temp_gender = gender if(temp_gender != PLURAL && temp_gender != NEUTER) diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index f90a9c4e5adf..ae7704459b5d 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -157,6 +157,17 @@ if(degree < 315) return WEST return NORTH|WEST +/proc/angle2dir_cardinal(angle) + switch(round(angle, 0.1)) + if(315.5 to 360, 0 to 45.5) + return NORTH + if(45.6 to 135.5) + return EAST + if(135.6 to 225.5) + return SOUTH + if(225.6 to 315.5) + return WEST + //returns the north-zero clockwise angle in degrees, given a direction /proc/dir2angle(var/D) diff --git a/code/__HELPERS/unique_ids.dm b/code/__HELPERS/unique_ids.dm index 1e9c699dcb97..a66de0b5a884 100644 --- a/code/__HELPERS/unique_ids.dm +++ b/code/__HELPERS/unique_ids.dm @@ -16,7 +16,7 @@ var/global/next_unique_datum_id = 1 -/client/var/tmp/unique_datum_id = null +// /client/var/tmp/unique_datum_id = null /datum/proc/UID() if(!unique_datum_id) @@ -26,12 +26,6 @@ var/global/next_unique_datum_id = 1 tag = tag_backup return unique_datum_id -/client/proc/UID() - if(!unique_datum_id) - unique_datum_id = "\ref[src]_[next_unique_datum_id++]" - return unique_datum_id - - /proc/locateUID(uid) if(!istext(uid)) return null diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index f7c5b670dc8c..10759fe336af 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1,2065 +1,2065 @@ -/* - * A large number of misc global procs. - */ - - /* Get the direction of startObj relative to endObj. - * Return values: To the right, 1. Below, 2. To the left, 3. Above, 4. Not found adjacent in cardinal directions, 0. - */ -/proc/getRelativeDirection(var/atom/movable/startObj, var/atom/movable/endObj) - if(endObj.x == startObj.x + 1 && endObj.y == startObj.y) - return EAST - - if(endObj.x == startObj.x - 1 && endObj.y == startObj.y) - return WEST - - if(endObj.y == startObj.y + 1 && endObj.x == startObj.x) - return NORTH - - if(endObj.y == startObj.y - 1 && endObj.x == startObj.x) - return SOUTH - - return 0 - -//Inverts the colour of an HTML string -/proc/invertHTML(HTMLstring) - - if(!( istext(HTMLstring) )) - CRASH("Given non-text argument!") - return - else - if(length(HTMLstring) != 7) - CRASH("Given non-HTML argument!") - return - var/textr = copytext(HTMLstring, 2, 4) - var/textg = copytext(HTMLstring, 4, 6) - var/textb = copytext(HTMLstring, 6, 8) - var/r = hex2num(textr) - var/g = hex2num(textg) - var/b = hex2num(textb) - textr = num2hex(255 - r) - textg = num2hex(255 - g) - textb = num2hex(255 - b) - if(length(textr) < 2) - textr = text("0[]", textr) - if(length(textg) < 2) - textr = text("0[]", textg) - if(length(textb) < 2) - textr = text("0[]", textb) - return text("#[][][]", textr, textg, textb) - return - -//Returns the middle-most value -/proc/dd_range(var/low, var/high, var/num) - return max(low,min(high,num)) - -//Returns whether or not A is the middle most value -/proc/InRange(var/A, var/lower, var/upper) - if(A < lower) return 0 - if(A > upper) return 0 - return 1 - - -/proc/Get_Angle(atom/movable/start,atom/movable/end)//For beams. - if(!start || !end) return 0 - var/dy - var/dx - dy=(32*end.y+end.pixel_y)-(32*start.y+start.pixel_y) - dx=(32*end.x+end.pixel_x)-(32*start.x+start.pixel_x) - if(!dy) - return (dx>=0)?90:270 - .=arctan(dx/dy) - if(dy<0) - .+=180 - else if(dx<0) - .+=360 - -//Returns location. Returns null if no location was found. -/proc/get_teleport_loc(turf/location,mob/target,distance = 1, density = 0, errorx = 0, errory = 0, eoffsetx = 0, eoffsety = 0) -/* -Location where the teleport begins, target that will teleport, distance to go, density checking 0/1(yes/no). -Random error in tile placement x, error in tile placement y, and block offset. -Block offset tells the proc how to place the box. Behind teleport location, relative to starting location, forward, etc. -Negative values for offset are accepted, think of it in relation to North, -x is west, -y is south. Error defaults to positive. -Turf and target are seperate in case you want to teleport some distance from a turf the target is not standing on or something. -*/ - - var/dirx = 0//Generic location finding variable. - var/diry = 0 - - var/xoffset = 0//Generic counter for offset location. - var/yoffset = 0 - - var/b1xerror = 0//Generic placing for point A in box. The lower left. - var/b1yerror = 0 - var/b2xerror = 0//Generic placing for point B in box. The upper right. - var/b2yerror = 0 - - errorx = abs(errorx)//Error should never be negative. - errory = abs(errory) - //var/errorxy = round((errorx+errory)/2)//Used for diagonal boxes. - - switch(target.dir)//This can be done through equations but switch is the simpler method. And works fast to boot. - //Directs on what values need modifying. - if(1)//North - diry+=distance - yoffset+=eoffsety - xoffset+=eoffsetx - b1xerror-=errorx - b1yerror-=errory - b2xerror+=errorx - b2yerror+=errory - if(2)//South - diry-=distance - yoffset-=eoffsety - xoffset+=eoffsetx - b1xerror-=errorx - b1yerror-=errory - b2xerror+=errorx - b2yerror+=errory - if(4)//East - dirx+=distance - yoffset+=eoffsetx//Flipped. - xoffset+=eoffsety - b1xerror-=errory//Flipped. - b1yerror-=errorx - b2xerror+=errory - b2yerror+=errorx - if(8)//West - dirx-=distance - yoffset-=eoffsetx//Flipped. - xoffset+=eoffsety - b1xerror-=errory//Flipped. - b1yerror-=errorx - b2xerror+=errory - b2yerror+=errorx - - var/turf/destination=locate(location.x+dirx,location.y+diry,location.z) - - if(destination)//If there is a destination. - if(errorx||errory)//If errorx or y were specified. - var/destination_list[] = list()//To add turfs to list. - //destination_list = new() - /*This will draw a block around the target turf, given what the error is. - Specifying the values above will basically draw a different sort of block. - If the values are the same, it will be a square. If they are different, it will be a rectengle. - In either case, it will center based on offset. Offset is position from center. - Offset always calculates in relation to direction faced. In other words, depending on the direction of the teleport, - the offset should remain positioned in relation to destination.*/ - - var/turf/center = locate((destination.x+xoffset),(destination.y+yoffset),location.z)//So now, find the new center. - - //Now to find a box from center location and make that our destination. - for(var/turf/T in block(locate(center.x+b1xerror,center.y+b1yerror,location.z), locate(center.x+b2xerror,center.y+b2yerror,location.z) )) - if(density&&T.density) continue//If density was specified. - if(T.x>world.maxx || T.x<1) continue//Don't want them to teleport off the map. - if(T.y>world.maxy || T.y<1) continue - destination_list += T - if(destination_list.len) - destination = pick(destination_list) - else return - - else//Same deal here. - if(density&&destination.density) return - if(destination.x>world.maxx || destination.x<1) return - if(destination.y>world.maxy || destination.y<1) return - else return - - return destination - -// Returns true if direction is blocked from loc -// Checks if doors are open -/proc/DirBlocked(turf/loc,var/dir) - for(var/obj/structure/window/D in loc) - if(!D.density) - continue - if(D.fulltile) - return 1 - if(D.dir == dir) - return 1 - - for(var/obj/machinery/door/D in loc) - if(!D.density)//if the door is open - continue - else return 1 // if closed, it's a real, air blocking door - return 0 - -///////////////////////////////////////////////////////////////////////// - -/proc/getline(atom/M,atom/N)//Ultra-Fast Bresenham Line-Drawing Algorithm - var/px=M.x //starting x - var/py=M.y - var/line[] = list(locate(px,py,M.z)) - var/dx=N.x-px //x distance - var/dy=N.y-py - var/dxabs=abs(dx)//Absolute value of x distance - var/dyabs=abs(dy) - var/sdx=SIGN(dx) //Sign of x distance (+ or -) - var/sdy=SIGN(dy) - var/x=dxabs>>1 //Counters for steps taken, setting to distance/2 - var/y=dyabs>>1 //Bit-shifting makes me l33t. It also makes getline() unnessecarrily fast. - var/j //Generic integer for counting - if(dxabs>=dyabs) //x distance is greater than y - for(j=0;j=dxabs) //Every dyabs steps, step once in y direction - y-=dxabs - py+=sdy - px+=sdx //Step on in x direction - line+=locate(px,py,M.z)//Add the turf to the list - else - for(j=0;j=dyabs) - x-=dyabs - px+=sdx - py+=sdy - line+=locate(px,py,M.z) - return line - -//Same as the thing below just for density and without support for atoms. -/proc/can_line(atom/source, atom/target, length = 5) - var/turf/current = get_turf(source) - var/turf/target_turf = get_turf(target) - var/steps = 0 - - while(current != target_turf) - if(steps > length) - return FALSE - if(!current) - return FALSE - if(current.density) - return FALSE - current = get_step_towards(current, target_turf) - steps++ - return TRUE - -//Returns whether or not a player is a guest using their ckey as an input -/proc/IsGuestKey(key) - if(findtext(key, "Guest-", 1, 7) != 1) //was findtextEx - return 0 - - var/i, ch, len = length(key) - - for(i = 7, i <= len, ++i) - ch = text2ascii(key, i) - if(ch < 48 || ch > 57) - return 0 - return 1 - -//Ensure the frequency is within bounds of what it should be sending/recieving at -/proc/sanitize_frequency(var/f, var/low = PUBLIC_LOW_FREQ, var/high = PUBLIC_HIGH_FREQ) - f = round(f) - f = max(low, f) - f = min(high, f) - if((f % 2) == 0) //Ensure the last digit is an odd number - f += 1 - return f - -//Turns 1479 into 147.9 -/proc/format_frequency(var/f) - return "[round(f / 10)].[f % 10]" - -/obj/proc/atmosanalyzer_scan(var/datum/gas_mixture/air_contents, mob/user, var/obj/target = src) - var/obj/icon = target - user.visible_message("[user] has used the analyzer on [target].", "You use the analyzer on [target].") - var/pressure = air_contents.return_pressure() - var/total_moles = air_contents.total_moles() - - user.show_message("Results of analysis of [bicon(icon)] [target].", 1) - if(total_moles>0) - var/o2_concentration = air_contents.oxygen/total_moles - var/n2_concentration = air_contents.nitrogen/total_moles - var/co2_concentration = air_contents.carbon_dioxide/total_moles - var/plasma_concentration = air_contents.toxins/total_moles - - var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_concentration) - - user.show_message("Pressure: [round(pressure,0.1)] kPa", 1) - user.show_message("Nitrogen: [round(n2_concentration*100)] % ([round(air_contents.nitrogen,0.01)] moles)", 1) - user.show_message("Oxygen: [round(o2_concentration*100)] % ([round(air_contents.oxygen,0.01)] moles)", 1) - user.show_message("CO2: [round(co2_concentration*100)] % ([round(air_contents.carbon_dioxide,0.01)] moles)", 1) - user.show_message("Plasma: [round(plasma_concentration*100)] % ([round(air_contents.toxins,0.01)] moles)", 1) - if(unknown_concentration>0.01) - user.show_message("Unknown: [round(unknown_concentration*100)] % ([round(unknown_concentration*total_moles,0.01)] moles)", 1) - user.show_message("Total: [round(total_moles,0.01)] moles", 1) - user.show_message("Temperature: [round(air_contents.temperature-T0C)] °C", 1) - else - user.show_message("[target] is empty!", 1) - return - -//Picks a string of symbols to display as the law number for hacked or ion laws -/proc/ionnum() - return "[pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")]" - -//When an AI is activated, it can choose from a list of non-slaved borgs to have as a slave. -/proc/freeborg() - var/select = null - var/list/borgs = list() - for(var/mob/living/silicon/robot/A in GLOB.player_list) - if(A.stat == 2 || A.connected_ai || A.scrambledcodes || istype(A,/mob/living/silicon/robot/drone)) - continue - var/name = "[A.real_name] ([A.modtype] [A.braintype])" - borgs[name] = A - - if(borgs.len) - select = input("Unshackled borg signals detected:", "Borg selection", null, null) as null|anything in borgs - return borgs[select] - -//When a borg is activated, it can choose which AI it wants to be slaved to -/proc/active_ais() - . = list() - for(var/mob/living/silicon/ai/A in GLOB.living_mob_list) - if(A.stat == DEAD) - continue - if(A.control_disabled == 1) - continue - . += A - return . - -//Find an active ai with the least borgs. VERBOSE PROCNAME HUH! -/proc/select_active_ai_with_fewest_borgs() - var/mob/living/silicon/ai/selected - var/list/active = active_ais() - for(var/mob/living/silicon/ai/A in active) - if(!selected || (selected.connected_robots > A.connected_robots)) - selected = A - - return selected - -/proc/select_active_ai(var/mob/user) - var/list/ais = active_ais() - if(ais.len) - if(user) . = input(usr,"AI signals detected:", "AI selection") in ais - else . = pick(ais) - return . - -/proc/get_sorted_mobs() - var/list/old_list = getmobs() - var/list/AI_list = list() - var/list/Dead_list = list() - var/list/keyclient_list = list() - var/list/key_list = list() - var/list/logged_list = list() - for(var/named in old_list) - var/mob/M = old_list[named] - if(issilicon(M)) - AI_list |= M - else if(isobserver(M) || M.stat == DEAD) - Dead_list |= M - else if(M.key && M.client) - keyclient_list |= M - else if(M.key) - key_list |= M - else - logged_list |= M - old_list.Remove(named) - var/list/new_list = list() - new_list += AI_list - new_list += keyclient_list - new_list += key_list - new_list += logged_list - new_list += Dead_list - return new_list - -//Returns a list of all mobs with their name -/proc/getmobs() - - var/list/mobs = sortmobs() - var/list/names = list() - var/list/creatures = list() - var/list/namecounts = list() - for(var/mob/M in mobs) - var/name = M.name - if(name in names) - namecounts[name]++ - name = "[name] ([namecounts[name]])" - else - names.Add(name) - namecounts[name] = 1 - if(M.real_name && M.real_name != M.name) - name += " \[[M.real_name]\]" - if(M.stat == DEAD) - if(istype(M, /mob/dead/observer/)) - name += " \[ghost\]" - else - name += " \[dead\]" - creatures[name] = M - - return creatures - -//Orders mobs by type then by name -/proc/sortmobs() - var/list/moblist = list() - var/list/sortmob = sortAtom(GLOB.mob_list) - for(var/mob/living/silicon/ai/M in sortmob) - moblist.Add(M) - if(M.eyeobj) - moblist.Add(M.eyeobj) - for(var/mob/living/silicon/pai/M in sortmob) - moblist.Add(M) - for(var/mob/living/silicon/robot/M in sortmob) - moblist.Add(M) - for(var/mob/living/carbon/human/M in sortmob) - moblist.Add(M) - for(var/mob/living/carbon/brain/M in sortmob) - moblist.Add(M) - for(var/mob/living/carbon/alien/M in sortmob) - moblist.Add(M) - for(var/mob/dead/observer/M in sortmob) - moblist.Add(M) - for(var/mob/new_player/M in sortmob) - moblist.Add(M) - for(var/mob/living/carbon/slime/M in sortmob) - moblist.Add(M) - for(var/mob/living/simple_animal/M in sortmob) - moblist.Add(M) - return moblist - -// Format a power value in W, kW, MW, or GW. -/proc/DisplayPower(powerused) - if(powerused < 1000) //Less than a kW - return "[powerused] W" - else if(powerused < 1000000) //Less than a MW - return "[round((powerused * 0.001), 0.01)] kW" - else if(powerused < 1000000000) //Less than a GW - return "[round((powerused * 0.000001), 0.001)] MW" - return "[round((powerused * 0.000000001), 0.0001)] GW" - -//E = MC^2 -/proc/convert2energy(var/M) - var/E = M*(SPEED_OF_LIGHT_SQ) - return E - -//M = E/C^2 -/proc/convert2mass(var/E) - var/M = E/(SPEED_OF_LIGHT_SQ) - return M - -//Forces a variable to be posative -/proc/modulus(var/M) - if(M >= 0) - return M - if(M < 0) - return -M - -/proc/get_mob_by_ckey(key) - if(!key) - return - for(var/mob/M in GLOB.mob_list) - if(M.ckey == key) - return M - -// Returns the atom sitting on the turf. -// For example, using this on a disk, which is in a bag, on a mob, will return the mob because it's on the turf. -/proc/get_atom_on_turf(var/atom/movable/M) - var/atom/loc = M - while(loc && loc.loc && !istype(loc.loc, /turf/)) - loc = loc.loc - return loc - -/* -Returns 1 if the chain up to the area contains the given typepath -0 otherwise -*/ -/atom/proc/is_found_within(var/typepath) - var/atom/A = src - while(A.loc) - if(istype(A.loc, typepath)) - return 1 - A = A.loc - return 0 - -// the on-close client verb -// called when a browser popup window is closed after registering with proc/onclose() -// if a valid atom reference is supplied, call the atom's Topic() with "close=1" -// otherwise, just reset the client mob's machine var. - - -// returns the turf located at the map edge in the specified direction relative to A -// used for mass driver -/proc/get_edge_target_turf(var/atom/A, var/direction) - - var/turf/target = locate(A.x, A.y, A.z) - if(!A || !target) - return 0 - //since NORTHEAST == NORTH & EAST, etc, doing it this way allows for diagonal mass drivers in the future - //and isn't really any more complicated - - // Note diagonal directions won't usually be accurate - if(direction & NORTH) - target = locate(target.x, world.maxy, target.z) - if(direction & SOUTH) - target = locate(target.x, 1, target.z) - if(direction & EAST) - target = locate(world.maxx, target.y, target.z) - if(direction & WEST) - target = locate(1, target.y, target.z) - - return target - -// returns turf relative to A in given direction at set range -// result is bounded to map size -// note range is non-pythagorean -// used for disposal system -/proc/get_ranged_target_turf(var/atom/A, var/direction, var/range) - - var/x = A.x - var/y = A.y - if(direction & NORTH) - y = min(world.maxy, y + range) - if(direction & SOUTH) - y = max(1, y - range) - if(direction & EAST) - x = min(world.maxx, x + range) - if(direction & WEST) - x = max(1, x - range) - - return locate(x,y,A.z) - - -// returns turf relative to A offset in dx and dy tiles -// bound to map limits -/proc/get_offset_target_turf(var/atom/A, var/dx, var/dy) - var/x = min(world.maxx, max(1, A.x + dx)) - var/y = min(world.maxy, max(1, A.y + dy)) - return locate(x,y,A.z) - -//Makes sure MIDDLE is between LOW and HIGH. If not, it adjusts it. Returns the adjusted value. -/proc/between(var/low, var/middle, var/high) - return max(min(middle, high), low) - -proc/arctan(x) - var/y=arcsin(x/sqrt(1+x*x)) - return y - -//returns random gauss number -proc/GaussRand(var/sigma) - var/x,y,rsq - do - x=2*rand()-1 - y=2*rand()-1 - rsq=x*x+y*y - while(rsq>1 || !rsq) - return sigma*y*sqrt(-2*log(rsq)/rsq) - -//returns random gauss number, rounded to 'roundto' -proc/GaussRandRound(var/sigma,var/roundto) - return round(GaussRand(sigma),roundto) - -//Will return the contents of an atom recursivly to a depth of 'searchDepth' -/atom/proc/GetAllContents(searchDepth = 5) - var/list/toReturn = list() - - for(var/atom/part in contents) - toReturn += part - if(part.contents.len && searchDepth) - toReturn += part.GetAllContents(searchDepth - 1) - - return toReturn - -//Searches contents of the atom and returns the sum of all w_class of obj/item within -/atom/proc/GetTotalContentsWeight(searchDepth = 5) - var/weight = 0 - var/list/content = GetAllContents(searchDepth) - for(var/obj/item/I in content) - weight += I.w_class - return weight - -//Step-towards method of determining whether one atom can see another. Similar to viewers() -/proc/can_see(var/atom/source, var/atom/target, var/length=5) // I couldnt be arsed to do actual raycasting :I This is horribly inaccurate. - var/turf/current = get_turf(source) - var/turf/target_turf = get_turf(target) - var/steps = 1 - - if(current != target_turf) - current = get_step_towards(current, target_turf) - while(current != target_turf) - if(steps > length) - return 0 - if(current.opacity) - return 0 - for(var/thing in current) - var/atom/A = thing - if(A.opacity) - return 0 - current = get_step_towards(current, target_turf) - steps++ - - return 1 - -proc/is_blocked_turf(turf/T, exclude_mobs) - if(T.density) - return 1 - for(var/i in T) - var/atom/A = i - if(A.density && (!exclude_mobs || !ismob(A))) - return 1 - return 0 - -/proc/get_step_towards2(var/atom/ref , var/atom/trg) - var/base_dir = get_dir(ref, get_step_towards(ref,trg)) - var/turf/temp = get_step_towards(ref,trg) - - if(is_blocked_turf(temp)) - var/dir_alt1 = turn(base_dir, 90) - var/dir_alt2 = turn(base_dir, -90) - var/turf/turf_last1 = temp - var/turf/turf_last2 = temp - var/free_tile = null - var/breakpoint = 0 - - while(!free_tile && breakpoint < 10) - if(!is_blocked_turf(turf_last1)) - free_tile = turf_last1 - break - if(!is_blocked_turf(turf_last2)) - free_tile = turf_last2 - break - turf_last1 = get_step(turf_last1,dir_alt1) - turf_last2 = get_step(turf_last2,dir_alt2) - breakpoint++ - - if(!free_tile) return get_step(ref, base_dir) - else return get_step_towards(ref,free_tile) - - else return get_step(ref, base_dir) - -//Takes: Anything that could possibly have variables and a varname to check. -//Returns: 1 if found, 0 if not. -/proc/hasvar(var/datum/A, var/varname) - if(A.vars.Find(lowertext(varname))) return 1 - else return 0 - -//Returns: all the areas in the world -/proc/return_areas() - var/list/area/areas = list() - for(var/area/A in world) - areas += A - return areas - -//Returns: all the areas in the world, sorted. -/proc/return_sorted_areas() - return sortAtom(return_areas()) - -//Takes: Area type as text string or as typepath OR an instance of the area. -//Returns: A list of all areas of that type in the world. -/proc/get_areas(var/areatype) - if(!areatype) return null - if(istext(areatype)) areatype = text2path(areatype) - if(isarea(areatype)) - var/area/areatemp = areatype - areatype = areatemp.type - - var/list/areas = new/list() - for(var/area/N in world) - if(istype(N, areatype)) areas += N - return areas - -//Takes: Area type as text string or as typepath OR an instance of the area. -//Returns: A list of all turfs in areas of that type of that type in the world. -/proc/get_area_turfs(var/areatype) - if(!areatype) return null - if(istext(areatype)) areatype = text2path(areatype) - if(isarea(areatype)) - var/area/areatemp = areatype - areatype = areatemp.type - - var/list/turfs = new/list() - for(var/area/N in world) - if(istype(N, areatype)) - for(var/turf/T in N) turfs += T - return turfs - -//Takes: Area type as text string or as typepath OR an instance of the area. -//Returns: A list of all atoms (objs, turfs, mobs) in areas of that type of that type in the world. -/proc/get_area_all_atoms(var/areatype) - if(!areatype) return null - if(istext(areatype)) areatype = text2path(areatype) - if(isarea(areatype)) - var/area/areatemp = areatype - areatype = areatemp.type - - var/list/atoms = new/list() - for(var/area/N in world) - if(istype(N, areatype)) - for(var/atom/A in N) - atoms += A - return atoms - -/datum/coords //Simple datum for storing coordinates. - var/x_pos = null - var/y_pos = null - var/z_pos = null - -/area/proc/move_contents_to(var/area/A, var/turftoleave=null, var/direction = null) - //Takes: Area. Optional: turf type to leave behind. - //Returns: Nothing. - //Notes: Attempts to move the contents of one area to another area. - // Movement based on lower left corner. Tiles that do not fit - // into the new area will not be moved. - - if(!A || !src) return 0 - - var/list/turfs_src = get_area_turfs(src.type) - var/list/turfs_trg = get_area_turfs(A.type) - - var/src_min_x = 0 - var/src_min_y = 0 - for(var/turf/T in turfs_src) - if(T.x < src_min_x || !src_min_x) src_min_x = T.x - if(T.y < src_min_y || !src_min_y) src_min_y = T.y - - var/trg_min_x = 0 - var/trg_min_y = 0 - for(var/turf/T in turfs_trg) - if(T.x < trg_min_x || !trg_min_x) trg_min_x = T.x - if(T.y < trg_min_y || !trg_min_y) trg_min_y = T.y - - var/list/refined_src = new/list() - for(var/turf/T in turfs_src) - refined_src += T - refined_src[T] = new/datum/coords - var/datum/coords/C = refined_src[T] - C.x_pos = (T.x - src_min_x) - C.y_pos = (T.y - src_min_y) - - var/list/refined_trg = new/list() - for(var/turf/T in turfs_trg) - refined_trg += T - refined_trg[T] = new/datum/coords - var/datum/coords/C = refined_trg[T] - C.x_pos = (T.x - trg_min_x) - C.y_pos = (T.y - trg_min_y) - - var/list/fromupdate = new/list() - var/list/toupdate = new/list() - - moving: - for(var/turf/T in refined_src) - var/datum/coords/C_src = refined_src[T] - for(var/turf/B in refined_trg) - var/datum/coords/C_trg = refined_trg[B] - if(C_src.x_pos == C_trg.x_pos && C_src.y_pos == C_trg.y_pos) - - var/old_dir1 = T.dir - var/old_icon_state1 = T.icon_state - var/old_icon1 = T.icon - - var/turf/X = B.ChangeTurf(T.type) - X.dir = old_dir1 - X.icon_state = old_icon_state1 - X.icon = old_icon1 //Shuttle floors are in shuttle.dmi while the defaults are floors.dmi - - // Give the new turf our air, if simulated - if(istype(X, /turf/simulated) && istype(T, /turf/simulated)) - var/turf/simulated/sim = X - sim.copy_air_with_tile(T) - - - /* Quick visual fix for some weird shuttle corner artefacts when on transit space tiles */ - if(direction && findtext(X.icon_state, "swall_s")) - - // Spawn a new shuttle corner object - var/obj/corner = new() - corner.loc = X - corner.density = 1 - corner.anchored = 1 - corner.icon = X.icon - corner.icon_state = replacetext(X.icon_state, "_s", "_f") - corner.tag = "delete me" - corner.name = "wall" - - // Find a new turf to take on the property of - var/turf/nextturf = get_step(corner, direction) - if(!nextturf || !istype(nextturf, /turf/space)) - nextturf = get_step(corner, turn(direction, 180)) - - - // Take on the icon of a neighboring scrolling space icon - X.icon = nextturf.icon - X.icon_state = nextturf.icon_state - - - for(var/obj/O in T) - - // Reset the shuttle corners - if(O.tag == "delete me") - X.icon = 'icons/turf/shuttle.dmi' - X.icon_state = replacetext(O.icon_state, "_f", "_s") // revert the turf to the old icon_state - X.name = "wall" - qdel(O) // prevents multiple shuttle corners from stacking - continue - if(!istype(O,/obj)) continue - O.loc.Exited(O) - O.setLoc(X,teleported=1) - O.loc.Entered(O) - for(var/mob/M in T) - if(!M.move_on_shuttle) - continue - M.loc = X - -// var/area/AR = X.loc - -// if(AR.lighting_use_dynamic) //TODO: rewrite this code so it's not messed by lighting ~Carn -// X.opacity = !X.opacity -// X.set_opacity(!X.opacity) - - toupdate += X - - if(turftoleave) - fromupdate += T.ChangeTurf(turftoleave) - else - T.ChangeTurf(T.baseturf) - - refined_src -= T - refined_trg -= B - continue moving - - if(toupdate.len) - for(var/turf/simulated/T1 in toupdate) - SSair.remove_from_active(T1) - T1.CalculateAdjacentTurfs() - SSair.add_to_active(T1,1) - - if(fromupdate.len) - for(var/turf/simulated/T2 in fromupdate) - SSair.remove_from_active(T2) - T2.CalculateAdjacentTurfs() - SSair.add_to_active(T2,1) - - - - -/proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0, var/atom/newloc = null) - if(!original) - return null - - var/obj/O = null - - if(sameloc) - O=new original.type(original.loc) - else - O=new original.type(newloc) - - if(perfectcopy) - if((O) && (original)) - var/static/list/forbidden_vars = list("type","loc","locs","vars", "parent","parent_type", "verbs","ckey","key","power_supply","contents","reagents","stat","x","y","z","group") - - for(var/V in original.vars - forbidden_vars) - if(istype(original.vars[V],/list)) - var/list/L = original.vars[V] - O.vars[V] = L.Copy() - else if(istype(original.vars[V],/datum)) - continue // this would reference the original's object, that will break when it is used or deleted. - else - O.vars[V] = original.vars[V] - if(istype(O)) - O.update_icon() - return O - -/area/proc/copy_contents_to(var/area/A , var/platingRequired = 0 ) - //Takes: Area. Optional: If it should copy to areas that don't have plating - //Returns: Nothing. - //Notes: Attempts to move the contents of one area to another area. - // Movement based on lower left corner. Tiles that do not fit - // into the new area will not be moved. - - if(!A || !src) return 0 - - var/list/turfs_src = get_area_turfs(src.type) - var/list/turfs_trg = get_area_turfs(A.type) - - var/src_min_x = 0 - var/src_min_y = 0 - for(var/turf/T in turfs_src) - if(T.x < src_min_x || !src_min_x) src_min_x = T.x - if(T.y < src_min_y || !src_min_y) src_min_y = T.y - - var/trg_min_x = 0 - var/trg_min_y = 0 - for(var/turf/T in turfs_trg) - if(T.x < trg_min_x || !trg_min_x) trg_min_x = T.x - if(T.y < trg_min_y || !trg_min_y) trg_min_y = T.y - - var/list/refined_src = new/list() - for(var/turf/T in turfs_src) - refined_src += T - refined_src[T] = new/datum/coords - var/datum/coords/C = refined_src[T] - C.x_pos = (T.x - src_min_x) - C.y_pos = (T.y - src_min_y) - - var/list/refined_trg = new/list() - for(var/turf/T in turfs_trg) - refined_trg += T - refined_trg[T] = new/datum/coords - var/datum/coords/C = refined_trg[T] - C.x_pos = (T.x - trg_min_x) - C.y_pos = (T.y - trg_min_y) - - var/list/toupdate = new/list() - - var/copiedobjs = list() - - - moving: - for(var/turf/T in refined_src) - var/datum/coords/C_src = refined_src[T] - for(var/turf/B in refined_trg) - var/datum/coords/C_trg = refined_trg[B] - if(C_src.x_pos == C_trg.x_pos && C_src.y_pos == C_trg.y_pos) - - var/old_dir1 = T.dir - var/old_icon_state1 = T.icon_state - var/old_icon1 = T.icon - - if(platingRequired) - if(istype(B, /turf/space)) - continue moving - - var/turf/X = new T.type(B) - X.dir = old_dir1 - X.icon_state = old_icon_state1 - X.icon = old_icon1 //Shuttle floors are in shuttle.dmi while the defaults are floors.dmi - - - var/list/objs = new/list() - var/list/newobjs = new/list() - var/list/mobs = new/list() - var/list/newmobs = new/list() - - for(var/obj/O in T) - - if(!istype(O,/obj)) - continue - - objs += O - - - for(var/obj/O in objs) - newobjs += DuplicateObject(O , 1) - - - for(var/obj/O in newobjs) - O.loc = X - - for(var/mob/M in T) - - if(!M.move_on_shuttle) - continue - mobs += M - - for(var/mob/M in mobs) - newmobs += DuplicateObject(M , 1) - - for(var/mob/M in newmobs) - M.loc = X - - copiedobjs += newobjs - copiedobjs += newmobs - - - - for(var/V in T.vars) - if(!(V in list("type","loc","locs","vars", "parent", "parent_type","verbs","ckey","key","x","y","z","contents", "luminosity", "group"))) - X.vars[V] = T.vars[V] - -// var/area/AR = X.loc - -// if(AR.lighting_use_dynamic) -// X.opacity = !X.opacity -// X.sd_set_opacity(!X.opacity) //TODO: rewrite this code so it's not messed by lighting ~Carn - - toupdate += X - - refined_src -= T - refined_trg -= B - continue moving - - - - if(toupdate.len) - for(var/turf/simulated/T1 in toupdate) - T1.CalculateAdjacentTurfs() - SSair.add_to_active(T1,1) - - - return copiedobjs - - - -proc/get_cardinal_dir(atom/A, atom/B) - var/dx = abs(B.x - A.x) - var/dy = abs(B.y - A.y) - return get_dir(A, B) & (rand() * (dx+dy) < dy ? 3 : 12) - -//chances are 1:value. anyprob(1) will always return true -proc/anyprob(value) - return (rand(1,value)==value) - -proc/view_or_range(distance = world.view , center = usr , type) - switch(type) - if("view") - . = view(distance,center) - if("range") - . = range(distance,center) - return - -proc/oview_or_orange(distance = world.view , center = usr , type) - switch(type) - if("view") - . = oview(distance,center) - if("range") - . = orange(distance,center) - return - -proc/get_mob_with_client_list() - var/list/mobs = list() - for(var/mob/M in GLOB.mob_list) - if(M.client) - mobs += M - return mobs - - -/proc/parse_zone(zone) - if(zone == "r_hand") return "right hand" - else if(zone == "l_hand") return "left hand" - else if(zone == "l_arm") return "left arm" - else if(zone == "r_arm") return "right arm" - else if(zone == "l_leg") return "left leg" - else if(zone == "r_leg") return "right leg" - else if(zone == "l_foot") return "left foot" - else if(zone == "r_foot") return "right foot" - else if(zone == "l_hand") return "left hand" - else if(zone == "r_hand") return "right hand" - else if(zone == "l_foot") return "left foot" - else if(zone == "r_foot") return "right foot" - else return zone - -/* - - Gets the turf this atom's *ICON* appears to inhabit - It takes into account: - * Pixel_x/y - * Matrix x/y - - NOTE: if your atom has non-standard bounds then this proc - will handle it, but: - * if the bounds are even, then there are an even amount of "middle" turfs, the one to the EAST, NORTH, or BOTH is picked - (this may seem bad, but you're atleast as close to the center of the atom as possible, better than byond's default loc being all the way off) - * if the bounds are odd, the true middle turf of the atom is returned - -*/ - -/proc/get_turf_pixel(atom/movable/AM) - if(!istype(AM)) - return - - //Find AM's matrix so we can use it's X/Y pixel shifts - var/matrix/M = matrix(AM.transform) - - var/pixel_x_offset = AM.pixel_x + M.get_x_shift() - var/pixel_y_offset = AM.pixel_y + M.get_y_shift() - - //Irregular objects - if(AM.bound_height != world.icon_size || AM.bound_width != world.icon_size) - var/icon/AMicon = icon(AM.icon, AM.icon_state) - pixel_x_offset += ((AMicon.Width()/world.icon_size)-1)*(world.icon_size*0.5) - pixel_y_offset += ((AMicon.Height()/world.icon_size)-1)*(world.icon_size*0.5) - qdel(AMicon) - - //DY and DX - var/rough_x = round(round(pixel_x_offset,world.icon_size)/world.icon_size) - var/rough_y = round(round(pixel_y_offset,world.icon_size)/world.icon_size) - - //Find coordinates - var/turf/T = get_turf(AM) //use AM's turfs, as it's coords are the same as AM's AND AM's coords are lost if it is inside another atom - if(!T) - return null - var/final_x = T.x + rough_x - var/final_y = T.y + rough_y - - if(final_x || final_y) - return locate(final_x, final_y, T.z) - -//Finds the distance between two atoms, in pixels -//centered = 0 counts from turf edge to edge -//centered = 1 counts from turf center to turf center -//of course mathematically this is just adding world.icon_size on again -/proc/getPixelDistance(var/atom/A, var/atom/B, var/centered = 1) - if(!istype(A)||!istype(B)) - return 0 - . = bounds_dist(A, B) + sqrt((((A.pixel_x+B.pixel_x)**2) + ((A.pixel_y+B.pixel_y)**2))) - if(centered) - . += world.icon_size - -/proc/get(atom/loc, type) - while(loc) - if(istype(loc, type)) - return loc - loc = loc.loc - return null - -/proc/get_turf_or_move(turf/location) - return get_turf(location) - - -//For objects that should embed, but make no sense being is_sharp or is_pointed() -//e.g: rods -var/list/can_embed_types = typecacheof(list( - /obj/item/stack/rods, - /obj/item/pipe)) - -/proc/can_embed(obj/item/W) - if(is_sharp(W)) - return 1 - if(is_pointed(W)) - return 1 - - if(is_type_in_typecache(W, can_embed_types)) - return 1 - -//Quick type checks for some tools -var/global/list/common_tools = list( -/obj/item/stack/cable_coil, -/obj/item/wrench, -/obj/item/weldingtool, -/obj/item/screwdriver, -/obj/item/wirecutters, -/obj/item/multitool, -/obj/item/crowbar) - -/proc/istool(O) - if(O && is_type_in_list(O, common_tools)) - return 1 - return 0 - -/proc/iswrench(O) - if(istype(O, /obj/item/wrench)) - return 1 - return 0 - -/proc/iswelder(O) - if(istype(O, /obj/item/weldingtool)) - return 1 - return 0 - -/proc/iscoil(O) - if(istype(O, /obj/item/stack/cable_coil)) - return 1 - return 0 - -/proc/iswirecutter(O) - if(istype(O, /obj/item/wirecutters)) - return 1 - return 0 - -/proc/isscrewdriver(O) - if(istype(O, /obj/item/screwdriver)) - return 1 - return 0 - -/proc/ismultitool(O) - if(istype(O, /obj/item/multitool)) - return 1 - return 0 - -/proc/iscrowbar(O) - if(istype(O, /obj/item/crowbar)) - return 1 - return 0 - -/proc/ispowertool(O)//used to check if a tool can force powered doors - if(istype(O, /obj/item/crowbar/power) || istype(O, /obj/item/mecha_parts/mecha_equipment/medical/rescue_jaw)) - return TRUE - return FALSE - -/proc/iswire(O) - if(istype(O, /obj/item/stack/cable_coil)) - return 1 - return 0 - -/proc/is_hot(obj/item/W as obj) - if(istype(W, /obj/item/weldingtool)) - var/obj/item/weldingtool/O = W - if(O.isOn()) - return 2500 - else - return 0 - if(istype(W, /obj/item/lighter)) - var/obj/item/lighter/O = W - if(O.lit) - return 1500 - else - return 0 - if(istype(W, /obj/item/match)) - var/obj/item/match/O = W - if(O.lit == 1) - return 1000 - else - return 0 - if(istype(W, /obj/item/clothing/mask/cigarette)) - var/obj/item/clothing/mask/cigarette/O = W - if(O.lit) - return 1000 - else - return 0 - if(istype(W, /obj/item/candle)) - var/obj/item/candle/O = W - if(O.lit) - return 1000 - else - return 0 - if(istype(W, /obj/item/flashlight/flare)) - var/obj/item/flashlight/flare/O = W - if(O.on) - return 1000 - else - return 0 - if(istype(W, /obj/item/gun/energy/plasmacutter)) - return 3800 - if(istype(W, /obj/item/melee/energy)) - var/obj/item/melee/energy/O = W - if(O.active) - return 3500 - else - return 0 - if(istype(W, /obj/item/assembly/igniter)) - return 20000 - else - return 0 - -//Whether or not the given item counts as sharp in terms of dealing damage -/proc/is_sharp(obj/O) - if(!O) - return 0 - if(O.sharp) - return 1 - return 0 - -/proc/is_surgery_tool(obj/item/W as obj) - return ( \ - istype(W, /obj/item/scalpel) || \ - istype(W, /obj/item/hemostat) || \ - istype(W, /obj/item/retractor) || \ - istype(W, /obj/item/cautery) || \ - istype(W, /obj/item/bonegel) || \ - istype(W, /obj/item/bonesetter) - ) - -/proc/reverse_direction(var/dir) - switch(dir) - if(NORTH) - return SOUTH - if(NORTHEAST) - return SOUTHWEST - if(EAST) - return WEST - if(SOUTHEAST) - return NORTHWEST - if(SOUTH) - return NORTH - if(SOUTHWEST) - return NORTHEAST - if(WEST) - return EAST - if(NORTHWEST) - return SOUTHEAST - -/* -Checks if that loc and dir has a item on the wall -*/ -var/list/static/global/wall_items = typecacheof(list(/obj/machinery/power/apc, /obj/machinery/alarm, - /obj/item/radio/intercom, /obj/structure/extinguisher_cabinet, /obj/structure/reagent_dispensers/peppertank, - /obj/machinery/status_display, /obj/machinery/requests_console, /obj/machinery/light_switch, /obj/structure/sign, - /obj/machinery/newscaster, /obj/machinery/firealarm, /obj/structure/noticeboard, /obj/machinery/door_control, - /obj/machinery/computer/security/telescreen, /obj/machinery/embedded_controller/radio/airlock, - /obj/item/storage/secure/safe, /obj/machinery/door_timer, /obj/machinery/flasher, /obj/machinery/keycard_auth, - /obj/structure/mirror, /obj/structure/closet/fireaxecabinet, /obj/machinery/computer/security/telescreen/entertainment, - /obj/structure/sign)) - -/proc/gotwallitem(loc, dir) - for(var/obj/O in loc) - if(is_type_in_typecache(O, wall_items)) - //Direction works sometimes - if(O.dir == dir) - return 1 - - //Some stuff doesn't use dir properly, so we need to check pixel instead - switch(dir) - if(SOUTH) - if(O.pixel_y > 10) - return 1 - if(NORTH) - if(O.pixel_y < -10) - return 1 - if(WEST) - if(O.pixel_x > 10) - return 1 - if(EAST) - if(O.pixel_x < -10) - return 1 - - //Some stuff is placed directly on the wallturf (signs) - for(var/obj/O in get_step(loc, dir)) - if(is_type_in_typecache(O, wall_items)) - if(abs(O.pixel_x) <= 10 && abs(O.pixel_y) <= 10) - return 1 - return 0 - - -proc/get_angle(atom/a, atom/b) - return atan2(b.y - a.y, b.x - a.x) - -proc/atan2(x, y) - if(!x && !y) return 0 - return y >= 0 ? arccos(x / sqrt(x * x + y * y)) : -arccos(x / sqrt(x * x + y * y)) - -/proc/format_text(text) - return replacetext(replacetext(text,"\proper ",""),"\improper ","") - -/* -Standard way to write links -Sayu -*/ - -/proc/topic_link(var/datum/D, var/arglist, var/content) - if(istype(arglist,/list)) - arglist = list2params(arglist) - return "[content]" - - - -/proc/get_location_accessible(mob/M, location) - var/covered_locations = 0 //based on body_parts_covered - var/face_covered = 0 //based on flags_inv - var/eyesmouth_covered = 0 //based on flags_cover - if(iscarbon(M)) - var/mob/living/carbon/C = M - for(var/obj/item/clothing/I in list(C.back, C.wear_mask)) - covered_locations |= I.body_parts_covered - face_covered |= I.flags_inv - eyesmouth_covered |= I.flags_cover - if(ishuman(C)) - var/mob/living/carbon/human/H = C - for(var/obj/item/I in list(H.wear_suit, H.w_uniform, H.shoes, H.belt, H.gloves, H.glasses, H.head, H.r_ear, H.l_ear)) - covered_locations |= I.body_parts_covered - face_covered |= I.flags_inv - eyesmouth_covered |= I.flags_cover - - switch(location) - if("head") - if(covered_locations & HEAD) - return 0 - if("eyes") - if(face_covered & HIDEEYES || eyesmouth_covered & GLASSESCOVERSEYES || eyesmouth_covered & HEADCOVERSEYES) - return 0 - if("mouth") - if(covered_locations & HEAD || face_covered & HIDEFACE || eyesmouth_covered & MASKCOVERSMOUTH) - return 0 - if("chest") - if(covered_locations & UPPER_TORSO) - return 0 - if("groin") - if(covered_locations & LOWER_TORSO) - return 0 - if("l_arm") - if(covered_locations & ARM_LEFT) - return 0 - if("r_arm") - if(covered_locations & ARM_RIGHT) - return 0 - if("l_leg") - if(covered_locations & LEG_LEFT) - return 0 - if("r_leg") - if(covered_locations & LEG_RIGHT) - return 0 - if("l_hand") - if(covered_locations & HAND_LEFT) - return 0 - if("r_hand") - if(covered_locations & HAND_RIGHT) - return 0 - if("l_foot") - if(covered_locations & FOOT_LEFT) - return 0 - if("r_foot") - if(covered_locations & FOOT_RIGHT) - return 0 - - return 1 - -/proc/check_target_facings(mob/living/initator, mob/living/target) - /*This can be used to add additional effects on interactions between mobs depending on how the mobs are facing each other, such as adding a crit damage to blows to the back of a guy's head. - Given how click code currently works (Nov '13), the initiating mob will be facing the target mob most of the time - That said, this proc should not be used if the change facing proc of the click code is overriden at the same time*/ - if(!ismob(target) || target.lying) - //Make sure we are not doing this for things that can't have a logical direction to the players given that the target would be on their side - return FACING_FAILED - if(initator.dir == target.dir) //mobs are facing the same direction - return FACING_SAME_DIR - if(is_A_facing_B(initator, target) && is_A_facing_B(target, initator)) //mobs are facing each other - return FACING_EACHOTHER - if(initator.dir + 2 == target.dir || initator.dir - 2 == target.dir || initator.dir + 6 == target.dir || initator.dir - 6 == target.dir) //Initating mob is looking at the target, while the target mob is looking in a direction perpendicular to the 1st - return FACING_INIT_FACING_TARGET_TARGET_FACING_PERPENDICULAR - - -atom/proc/GetTypeInAllContents(typepath) - var/list/processing_list = list(src) - var/list/processed = list() - - var/atom/found = null - - while(processing_list.len && found==null) - var/atom/A = processing_list[1] - if(istype(A, typepath)) - found = A - - processing_list -= A - - for(var/atom/a in A) - if(!(a in processed)) - processing_list |= a - - processed |= A - - return found - -/proc/random_step(atom/movable/AM, steps, chance) - var/initial_chance = chance - while(steps > 0) - if(prob(chance)) - step(AM, pick(alldirs)) - chance = max(chance - (initial_chance / steps), 0) - steps-- - -/proc/get_random_colour(var/simple, var/lower, var/upper) - var/colour - if(simple) - colour = pick(list("FF0000","FF7F00","FFFF00","00FF00","0000FF","4B0082","8F00FF")) - else - for(var/i=1;i<=3;i++) - var/temp_col = "[num2hex(rand(lower,upper))]" - if(length(temp_col )<2) - temp_col = "0[temp_col]" - colour += temp_col - return colour - -/proc/get_distant_turf(var/turf/T,var/direction,var/distance) - if(!T || !direction || !distance) return - - var/dest_x = T.x - var/dest_y = T.y - var/dest_z = T.z - - if(direction & NORTH) - dest_y = min(world.maxy, dest_y+distance) - if(direction & SOUTH) - dest_y = max(0, dest_y-distance) - if(direction & EAST) - dest_x = min(world.maxy, dest_x+distance) - if(direction & WEST) - dest_x = max(0, dest_x-distance) - - return locate(dest_x,dest_y,dest_z) - -var/mob/dview/dview_mob = new - -//Version of view() which ignores darkness, because BYOND doesn't have it. -/proc/dview(var/range = world.view, var/center, var/invis_flags = 0) - if(!center) - return - - dview_mob.loc = center - - dview_mob.see_invisible = invis_flags - - . = view(range, dview_mob) - dview_mob.loc = null - -/mob/dview - invisibility = 101 - density = 0 - move_force = 0 - pull_force = 0 - move_resist = INFINITY - simulated = 0 - canmove = FALSE - see_in_dark = 1e6 - -/mob/dview/New() //For whatever reason, if this isn't called, then BYOND will throw a type mismatch runtime when attempting to add this to the mobs list. -Fox - -/mob/dview/Destroy() - // should never be deleted - return QDEL_HINT_LETMELIVE - -/proc/IsValidSrc(A) - if(istype(A, /datum)) - var/datum/D = A - return !QDELETED(D) - if(istype(A, /client)) - return TRUE - return FALSE - -//can a window be here, or is there a window blocking it? -/proc/valid_window_location(turf/T, dir_to_check) - if(!T) - return FALSE - for(var/obj/O in T) - if(istype(O, /obj/machinery/door/window) && (O.dir == dir_to_check || dir_to_check == FULLTILE_WINDOW_DIR)) - return FALSE - if(istype(O, /obj/structure/windoor_assembly)) - var/obj/structure/windoor_assembly/W = O - if(W.ini_dir == dir_to_check || dir_to_check == FULLTILE_WINDOW_DIR) - return FALSE - if(istype(O, /obj/structure/window)) - var/obj/structure/window/W = O - if(W.ini_dir == dir_to_check || W.ini_dir == FULLTILE_WINDOW_DIR || dir_to_check == FULLTILE_WINDOW_DIR) - return FALSE - return TRUE - -//Get the dir to the RIGHT of dir if they were on a clock -//NORTH --> NORTHEAST -/proc/get_clockwise_dir(dir) - . = angle2dir(dir2angle(dir)+45) - -//Get the dir to the LEFT of dir if they were on a clock -//NORTH --> NORTHWEST -/proc/get_anticlockwise_dir(dir) - . = angle2dir(dir2angle(dir)-45) - - -//Compare A's dir, the clockwise dir of A and the anticlockwise dir of A -//To the opposite dir of the dir returned by get_dir(B,A) -//If one of them is a match, then A is facing B -/proc/is_A_facing_B(atom/A, atom/B) - if(!istype(A) || !istype(B)) - return 0 - if(isliving(A)) - var/mob/living/LA = A - if(LA.lying) - return 0 - var/goal_dir = angle2dir(dir2angle(get_dir(B, A)+180)) - var/clockwise_A_dir = get_clockwise_dir(A.dir) - var/anticlockwise_A_dir = get_anticlockwise_dir(B.dir) - - if(A.dir == goal_dir || clockwise_A_dir == goal_dir || anticlockwise_A_dir == goal_dir) - return 1 - return 0 - -//This is just so you can stop an orbit. -//orbit() can run without it (swap orbiting for A) -//but then you can never stop it and that's just silly. -/atom/movable/var/atom/orbiting = null - -//A: atom to orbit -//radius: range to orbit at, radius of the circle formed by orbiting -//clockwise: whether you orbit clockwise or anti clockwise -//rotation_speed: how fast to rotate -//rotation_segments: the resolution of the orbit circle, less = a more block circle, this can be used to produce hexagons (6 segments) triangles (3 segments), and so on, 36 is the best default. -//pre_rotation: Chooses to rotate src 90 degress towards the orbit dir (clockwise/anticlockwise), useful for things to go "head first" like ghosts -//lockinorbit: Forces src to always be on A's turf, otherwise the orbit cancels when src gets too far away (eg: ghosts) - -/atom/movable/proc/orbit(atom/A, radius = 10, clockwise = FALSE, rotation_speed = 20, rotation_segments = 36, pre_rotation = TRUE, lockinorbit = FALSE, forceMove = FALSE) - if(!istype(A)) - return - - if(orbiting) - stop_orbit() - - orbiting = A - var/matrix/initial_transform = matrix(transform) - var/lastloc = loc - - //Head first! - if(pre_rotation) - var/matrix/M = matrix(transform) - var/pre_rot = 90 - if(!clockwise) - pre_rot = -90 - M.Turn(pre_rot) - transform = M - - var/matrix/shift = matrix(transform) - shift.Translate(0,radius) - transform = shift - - SpinAnimation(rotation_speed, -1, clockwise, rotation_segments) - - //we stack the orbits up client side, so we can assign this back to normal server side without it breaking the orbit - transform = initial_transform - while(orbiting && orbiting == A && A.loc) - var/targetloc = get_turf(A) - if(!lockinorbit && loc != lastloc && loc != targetloc) - break - if(forceMove) - forceMove(targetloc) - else - loc = targetloc - lastloc = loc - sleep(0.6) - - if(orbiting == A) //make sure we haven't started orbiting something else. - orbiting = null - SpinAnimation(0,0) - - - -/atom/movable/proc/stop_orbit() - orbiting = null - -//Centers an image. -//Requires: -//The Image -//The x dimension of the icon file used in the image -//The y dimension of the icon file used in the image -// eg: center_image(I, 32,32) -// eg2: center_image(I, 96,96) -/proc/center_image(var/image/I, x_dimension = 0, y_dimension = 0) - if(!I) - return - - if(!x_dimension || !y_dimension) - return - - //Get out of here, punk ass kids calling procs needlessly - if((x_dimension == world.icon_size) && (y_dimension == world.icon_size)) - return I - - //Offset the image so that it's bottom left corner is shifted this many pixels - //This makes it infinitely easier to draw larger inhands/images larger than world.iconsize - //but still use them in game - var/x_offset = -((x_dimension/world.icon_size)-1)*(world.icon_size*0.5) - var/y_offset = -((y_dimension/world.icon_size)-1)*(world.icon_size*0.5) - - //Correct values under world.icon_size - if(x_dimension < world.icon_size) - x_offset *= -1 - if(y_dimension < world.icon_size) - y_offset *= -1 - - I.pixel_x = x_offset - I.pixel_y = y_offset - - return I - -//similar function to RANGE_TURFS(), but will search spiralling outwards from the center (like the above, but only turfs) -/proc/spiral_range_turfs(dist=0, center=usr, orange=0) - if(!dist) - if(!orange) - return list(center) - else - return list() - - var/turf/t_center = get_turf(center) - if(!t_center) - return list() - - var/list/L = list() - var/turf/T - var/y - var/x - var/c_dist = 1 - - if(!orange) - L += t_center - - while( c_dist <= dist ) - y = t_center.y + c_dist - x = t_center.x - c_dist + 1 - for(x in x to t_center.x+c_dist) - T = locate(x,y,t_center.z) - if(T) - L += T - - y = t_center.y + c_dist - 1 - x = t_center.x + c_dist - for(y in t_center.y-c_dist to y) - T = locate(x,y,t_center.z) - if(T) - L += T - - y = t_center.y - c_dist - x = t_center.x + c_dist - 1 - for(x in t_center.x-c_dist to x) - T = locate(x,y,t_center.z) - if(T) - L += T - - y = t_center.y - c_dist + 1 - x = t_center.x - c_dist - for(y in y to t_center.y+c_dist) - T = locate(x,y,t_center.z) - if(T) - L += T - c_dist++ - - return L - -//ultra range (no limitations on distance, faster than range for distances > 8); including areas drastically decreases performance -/proc/urange(dist=0, atom/center=usr, orange=0, areas=0) - if(!dist) - if(!orange) - return list(center) - else - return list() - - var/list/turfs = RANGE_TURFS(dist, center) - if(orange) - turfs -= get_turf(center) - . = list() - for(var/V in turfs) - var/turf/T = V - . += T - . += T.contents - if(areas) - . |= T.loc - -/proc/turf_clear(turf/T) - for(var/atom/A in T) - if(A.simulated) - return FALSE - return TRUE - -/proc/screen_loc2turf(scr_loc, turf/origin) - var/tX = splittext(scr_loc, ",") - var/tY = splittext(tX[2], ":") - var/tZ = origin.z - tY = tY[1] - tX = splittext(tX[1], ":") - tX = tX[1] - tX = max(1, min(world.maxx, origin.x + (text2num(tX) - (world.view + 1)))) - tY = max(1, min(world.maxy, origin.y + (text2num(tY) - (world.view + 1)))) - return locate(tX, tY, tZ) - -/proc/get_closest_atom(type, list, source) - var/closest_atom - var/closest_distance - for(var/A in list) - if(!istype(A, type)) - continue - var/distance = get_dist(source, A) - if(!closest_distance) - closest_distance = distance - closest_atom = A - else - if(closest_distance > distance) - closest_distance = distance - closest_atom = A - return closest_atom - -/proc/pick_closest_path(value, list/matches = get_fancy_list_of_atom_types()) - if(value == FALSE) //nothing should be calling us with a number, so this is safe - value = input("Enter type to find (blank for all, cancel to cancel)", "Search for type") as null|text - if(isnull(value)) - return - value = trim(value) - if(!isnull(value) && value != "") - matches = filter_fancy_list(matches, value) - - if(matches.len == 0) - return - - var/chosen - if(matches.len == 1) - chosen = matches[1] - else - chosen = input("Select a type", "Pick Type", matches[1]) as null|anything in matches - if(!chosen) - return - chosen = matches[chosen] - return chosen - -/proc/make_types_fancy(var/list/types) - if(ispath(types)) - types = list(types) - . = list() - for(var/type in types) - var/typename = "[type]" - var/static/list/TYPES_SHORTCUTS = list( - //longest paths comes first - otherwise they get shadowed by the more generic ones - /obj/effect/decal/cleanable = "CLEANABLE", - /obj/effect = "EFFECT", - /obj/item/ammo_casing = "AMMO", - /obj/item/book/manual = "MANUAL", - /obj/item/borg/upgrade = "BORG_UPGRADE", - /obj/item/cartridge = "PDA_CART", - /obj/item/clothing/head/helmet/space = "SPESSHELMET", - /obj/item/clothing/head = "HEAD", - /obj/item/clothing/under = "UNIFORM", - /obj/item/clothing/shoes = "SHOES", - /obj/item/clothing/suit = "SUIT", - /obj/item/clothing/gloves = "GLOVES", - /obj/item/clothing/mask/cigarette = "CIGARRETE", // oof - /obj/item/clothing/mask = "MASK", - /obj/item/clothing/glasses = "GLASSES", - /obj/item/clothing = "CLOTHING", - /obj/item/grenade/clusterbuster = "CLUSTERBUSTER", - /obj/item/grenade = "GRENADE", - /obj/item/gun = "GUN", - /obj/item/implant = "IMPLANT", - /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack = "MECHA_MISSILE_RACK", - /obj/item/mecha_parts/mecha_equipment/weapon = "MECHA_WEAPON", - /obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP", - /obj/item/melee = "MELEE", - /obj/item/mmi = "MMI", - /obj/item/nullrod = "NULLROD", - /obj/item/organ/external = "EXT_ORG", - /obj/item/organ/internal/cyberimp = "CYBERIMP", - /obj/item/organ/internal = "INT_ORG", - /obj/item/organ = "ORGAN", - /obj/item/pda = "PDA", - /obj/item/projectile = "PROJ", - /obj/item/radio/headset = "HEADSET", - /obj/item/reagent_containers/glass/beaker = "BEAKER", - /obj/item/reagent_containers/glass/bottle = "BOTTLE", - /obj/item/reagent_containers/food/pill/patch = "PATCH", - /obj/item/reagent_containers/food/pill = "PILL", - /obj/item/reagent_containers/food/drinks = "DRINK", - /obj/item/reagent_containers/food = "FOOD", - /obj/item/reagent_containers/syringe = "SYRINGE", - /obj/item/reagent_containers = "REAGENT_CONTAINERS", - /obj/item/robot_parts = "ROBOT_PARTS", - /obj/item/seeds = "SEED", - /obj/item/slime_extract = "SLIME_CORE", - /obj/item/spacepod_equipment/weaponry = "POD_WEAPON", - /obj/item/spacepod_equipment = "POD_EQUIP", - /obj/item/stack/sheet/mineral = "MINERAL", - /obj/item/stack/sheet = "SHEET", - /obj/item/stack/tile = "TILE", - /obj/item/stack = "STACK", - /obj/item/stock_parts/cell = "POWERCELL", - /obj/item/stock_parts = "STOCK_PARTS", - /obj/item/storage/firstaid = "FIRSTAID", - /obj/item/storage = "STORAGE", - /obj/item/tank = "GAS_TANK", - /obj/item/toy/crayon = "CRAYON", - /obj/item/toy = "TOY", - /obj/item = "ITEM", - /obj/machinery/atmospherics = "ATMOS_MACH", - /obj/machinery/computer = "CONSOLE", - /obj/machinery/door/airlock = "AIRLOCK", - /obj/machinery/door = "DOOR", - /obj/machinery/kitchen_machine = "KITCHEN", - /obj/machinery/portable_atmospherics/canister = "CANISTER", - /obj/machinery/portable_atmospherics = "PORT_ATMOS", - /obj/machinery/power = "POWER", - /obj/machinery/telecomms = "TCOMMS", - /obj/machinery = "MACHINERY", - /obj/mecha = "MECHA", - /obj/structure/closet/crate = "CRATE", - /obj/structure/closet = "CLOSET", - /obj/structure/statue = "STATUE", - /obj/structure/chair = "CHAIR", // oh no - /obj/structure/bed = "BED", - /obj/structure/chair/stool = "STOOL", - /obj/structure/table = "TABLE", - /obj/structure = "STRUCTURE", - /obj/vehicle = "VEHICLE", - /obj = "O", - /datum = "D", - /turf/simulated/floor = "SIM_FLOOR", - /turf/simulated/wall = "SIM_WALL", - /turf/unsimulated/floor = "UNSIM_FLOOR", - /turf/unsimulated/wall = "UNSIM_WALL", - /turf = "T", - /mob/living/carbon/alien = "XENO", - /mob/living/carbon/human = "HUMAN", - /mob/living/carbon = "CARBON", - /mob/living/silicon/robot = "CYBORG", - /mob/living/silicon/ai = "AI", - /mob/living/silicon = "SILICON", - /mob/living/simple_animal/bot = "BOT", - /mob/living/simple_animal = "SIMPLE", - /mob/living = "LIVING", - /mob = "M" - ) - for(var/tn in TYPES_SHORTCUTS) - if(copytext(typename, 1, length("[tn]/") + 1) == "[tn]/") - typename = TYPES_SHORTCUTS[tn]+copytext(typename,length("[tn]/")) - break - .[typename] = type - - -/proc/get_fancy_list_of_atom_types() - var/static/list/pre_generated_list - if(!pre_generated_list) //init - pre_generated_list = make_types_fancy(typesof(/atom)) - return pre_generated_list - - -/proc/get_fancy_list_of_datum_types() - var/static/list/pre_generated_list - if(!pre_generated_list) //init - pre_generated_list = make_types_fancy(sortList(typesof(/datum) - typesof(/atom))) - return pre_generated_list - - -/proc/filter_fancy_list(list/L, filter as text) - var/list/matches = new - for(var/key in L) - var/value = L[key] - if(findtext("[key]", filter) || findtext("[value]", filter)) - matches[key] = value - return matches - -//Key thing that stops lag. Cornerstone of performance in ss13, Just sitting here, in unsorted.dm. - -//Increases delay as the server gets more overloaded, -//as sleeps aren't cheap and sleeping only to wake up and sleep again is wasteful -#define DELTA_CALC max(((max(TICK_USAGE, world.cpu) / 100) * max(Master.sleep_delta-1,1)), 1) - -//returns the number of ticks slept -/proc/stoplag(initial_delay) - if(!Master || !(Master.current_runlevel & RUNLEVELS_DEFAULT)) - sleep(world.tick_lag) - return 1 - if(!initial_delay) - initial_delay = world.tick_lag - . = 0 - var/i = DS2TICKS(initial_delay) - do - . += CEILING(i*DELTA_CALC, 1) - sleep(i*world.tick_lag*DELTA_CALC) - i *= 2 - while(TICK_USAGE > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit)) - -#undef DELTA_CALC - -// This proc gets a list of all "points of interest" (poi's) that can be used by admins to track valuable mobs or atoms (such as the nuke disk). -/proc/getpois(mobs_only=0,skip_mindless=0) - var/list/mobs = sortmobs() - var/list/names = list() - var/list/pois = list() - var/list/namecounts = list() - - for(var/mob/M in mobs) - if(skip_mindless && (!M.mind && !M.ckey)) - if(!isbot(M) && !istype(M, /mob/camera/)) - continue - if(M.client && M.client.holder && M.client.holder.fakekey) //stealthmins - continue - var/name = M.name - if(name in names) - namecounts[name]++ - name = "[name] ([namecounts[name]])" - else - names.Add(name) - namecounts[name] = 1 - if(M.real_name && M.real_name != M.name) - name += " \[[M.real_name]\]" - if(M.stat == DEAD) - if(istype(M, /mob/dead/observer/)) - name += " \[ghost\]" - else - name += " \[dead\]" - pois[name] = M - - if(!mobs_only) - for(var/atom/A in GLOB.poi_list) - if(!A || !A.loc) - continue - var/name = A.name - if(names.Find(name)) - namecounts[name]++ - name = "[name] ([namecounts[name]])" - else - names.Add(name) - namecounts[name] = 1 - pois[name] = A - - return pois - -/proc/flash_color(mob_or_client, flash_color="#960000", flash_time=20) - var/client/C - if(istype(mob_or_client, /mob)) - var/mob/M = mob_or_client - if(M.client) - C = M.client - else - return - else if(istype(mob_or_client, /client)) - C = mob_or_client - - if(!istype(C)) - return - - C.color = flash_color - spawn(0) - animate(C, color = initial(C.color), time = flash_time) - -#define RANDOM_COLOUR (rgb(rand(0,255),rand(0,255),rand(0,255))) - -/proc/make_bit_triplet() - var/list/num_sample = list(1, 2, 3, 4, 5, 6, 7, 8, 9) - var/result = 0 - for(var/i = 0, i < 3, i++) - var/num = pick(num_sample) - num_sample -= num - result += (1 << num) - return result - -/proc/pixel_shift_dir(var/dir, var/amount_x = 32, var/amount_y = 32) //Returns a list with pixel_shift values that will shift an object's icon one tile in the direction passed. - amount_x = min(max(0, amount_x), 32) //No less than 0, no greater than 32. - amount_y = min(max(0, amount_x), 32) - var/list/shift = list("x" = 0, "y" = 0) - switch(dir) - if(NORTH) - shift["y"] = amount_y - if(SOUTH) - shift["y"] = -amount_y - if(EAST) - shift["x"] = amount_x - if(WEST) - shift["x"] = -amount_x - if(NORTHEAST) - shift = list("x" = amount_x, "y" = amount_y) - if(NORTHWEST) - shift = list("x" = -amount_x, "y" = amount_y) - if(SOUTHEAST) - shift = list("x" = amount_x, "y" = -amount_y) - if(SOUTHWEST) - shift = list("x" = -amount_x, "y" = -amount_y) - - return shift - -//Return a list of atoms in a location of a given type. Can be refined to look for pixel-shift. -/proc/get_atoms_of_type(var/atom/here, var/type, var/check_shift, var/shift_x = 0, var/shift_y = 0) - . = list() - if(here) - for(var/atom/thing in here) - if(istype(thing, type) && (check_shift && thing.pixel_x == shift_x && thing.pixel_y == shift_y)) - . += thing - -//gives us the stack trace from CRASH() without ending the current proc. -/proc/stack_trace(msg) - CRASH(msg) - -/datum/proc/stack_trace(msg) - CRASH(msg) - -/proc/pass() - return - -/atom/proc/Shake(pixelshiftx = 15, pixelshifty = 15, duration = 250) - var/initialpixelx = pixel_x - var/initialpixely = pixel_y - var/shiftx = rand(-pixelshiftx,pixelshiftx) - var/shifty = rand(-pixelshifty,pixelshifty) - animate(src, pixel_x = pixel_x + shiftx, pixel_y = pixel_y + shifty, time = 0.2, loop = duration) - pixel_x = initialpixelx - pixel_y = initialpixely - -/proc/params2turf(scr_loc, turf/origin, client/C) - if(!scr_loc) - return null - var/tX = splittext(scr_loc, ",") - var/tY = splittext(tX[2], ":") - var/tZ = origin.z - tY = tY[1] - tX = splittext(tX[1], ":") - tX = tX[1] - var/list/actual_view = getviewsize(C ? C.view : world.view) - tX = Clamp(origin.x + text2num(tX) - round(actual_view[1] / 2) - 1, 1, world.maxx) - tY = Clamp(origin.y + text2num(tY) - round(actual_view[2] / 2) - 1, 1, world.maxy) - return locate(tX, tY, tZ) +/* + * A large number of misc global procs. + */ + + /* Get the direction of startObj relative to endObj. + * Return values: To the right, 1. Below, 2. To the left, 3. Above, 4. Not found adjacent in cardinal directions, 0. + */ +/proc/getRelativeDirection(var/atom/movable/startObj, var/atom/movable/endObj) + if(endObj.x == startObj.x + 1 && endObj.y == startObj.y) + return EAST + + if(endObj.x == startObj.x - 1 && endObj.y == startObj.y) + return WEST + + if(endObj.y == startObj.y + 1 && endObj.x == startObj.x) + return NORTH + + if(endObj.y == startObj.y - 1 && endObj.x == startObj.x) + return SOUTH + + return 0 + +//Inverts the colour of an HTML string +/proc/invertHTML(HTMLstring) + + if(!( istext(HTMLstring) )) + CRASH("Given non-text argument!") + return + else + if(length(HTMLstring) != 7) + CRASH("Given non-HTML argument!") + return + var/textr = copytext(HTMLstring, 2, 4) + var/textg = copytext(HTMLstring, 4, 6) + var/textb = copytext(HTMLstring, 6, 8) + var/r = hex2num(textr) + var/g = hex2num(textg) + var/b = hex2num(textb) + textr = num2hex(255 - r) + textg = num2hex(255 - g) + textb = num2hex(255 - b) + if(length(textr) < 2) + textr = text("0[]", textr) + if(length(textg) < 2) + textr = text("0[]", textg) + if(length(textb) < 2) + textr = text("0[]", textb) + return text("#[][][]", textr, textg, textb) + return + +//Returns the middle-most value +/proc/dd_range(var/low, var/high, var/num) + return max(low,min(high,num)) + +//Returns whether or not A is the middle most value +/proc/InRange(var/A, var/lower, var/upper) + if(A < lower) return 0 + if(A > upper) return 0 + return 1 + + +/proc/Get_Angle(atom/movable/start,atom/movable/end)//For beams. + if(!start || !end) return 0 + var/dy + var/dx + dy=(32*end.y+end.pixel_y)-(32*start.y+start.pixel_y) + dx=(32*end.x+end.pixel_x)-(32*start.x+start.pixel_x) + if(!dy) + return (dx>=0)?90:270 + .=arctan(dx/dy) + if(dy<0) + .+=180 + else if(dx<0) + .+=360 + +//Returns location. Returns null if no location was found. +/proc/get_teleport_loc(turf/location,mob/target,distance = 1, density = 0, errorx = 0, errory = 0, eoffsetx = 0, eoffsety = 0) +/* +Location where the teleport begins, target that will teleport, distance to go, density checking 0/1(yes/no). +Random error in tile placement x, error in tile placement y, and block offset. +Block offset tells the proc how to place the box. Behind teleport location, relative to starting location, forward, etc. +Negative values for offset are accepted, think of it in relation to North, -x is west, -y is south. Error defaults to positive. +Turf and target are seperate in case you want to teleport some distance from a turf the target is not standing on or something. +*/ + + var/dirx = 0//Generic location finding variable. + var/diry = 0 + + var/xoffset = 0//Generic counter for offset location. + var/yoffset = 0 + + var/b1xerror = 0//Generic placing for point A in box. The lower left. + var/b1yerror = 0 + var/b2xerror = 0//Generic placing for point B in box. The upper right. + var/b2yerror = 0 + + errorx = abs(errorx)//Error should never be negative. + errory = abs(errory) + //var/errorxy = round((errorx+errory)/2)//Used for diagonal boxes. + + switch(target.dir)//This can be done through equations but switch is the simpler method. And works fast to boot. + //Directs on what values need modifying. + if(1)//North + diry+=distance + yoffset+=eoffsety + xoffset+=eoffsetx + b1xerror-=errorx + b1yerror-=errory + b2xerror+=errorx + b2yerror+=errory + if(2)//South + diry-=distance + yoffset-=eoffsety + xoffset+=eoffsetx + b1xerror-=errorx + b1yerror-=errory + b2xerror+=errorx + b2yerror+=errory + if(4)//East + dirx+=distance + yoffset+=eoffsetx//Flipped. + xoffset+=eoffsety + b1xerror-=errory//Flipped. + b1yerror-=errorx + b2xerror+=errory + b2yerror+=errorx + if(8)//West + dirx-=distance + yoffset-=eoffsetx//Flipped. + xoffset+=eoffsety + b1xerror-=errory//Flipped. + b1yerror-=errorx + b2xerror+=errory + b2yerror+=errorx + + var/turf/destination=locate(location.x+dirx,location.y+diry,location.z) + + if(destination)//If there is a destination. + if(errorx||errory)//If errorx or y were specified. + var/destination_list[] = list()//To add turfs to list. + //destination_list = new() + /*This will draw a block around the target turf, given what the error is. + Specifying the values above will basically draw a different sort of block. + If the values are the same, it will be a square. If they are different, it will be a rectengle. + In either case, it will center based on offset. Offset is position from center. + Offset always calculates in relation to direction faced. In other words, depending on the direction of the teleport, + the offset should remain positioned in relation to destination.*/ + + var/turf/center = locate((destination.x+xoffset),(destination.y+yoffset),location.z)//So now, find the new center. + + //Now to find a box from center location and make that our destination. + for(var/turf/T in block(locate(center.x+b1xerror,center.y+b1yerror,location.z), locate(center.x+b2xerror,center.y+b2yerror,location.z) )) + if(density&&T.density) continue//If density was specified. + if(T.x>world.maxx || T.x<1) continue//Don't want them to teleport off the map. + if(T.y>world.maxy || T.y<1) continue + destination_list += T + if(destination_list.len) + destination = pick(destination_list) + else return + + else//Same deal here. + if(density&&destination.density) return + if(destination.x>world.maxx || destination.x<1) return + if(destination.y>world.maxy || destination.y<1) return + else return + + return destination + +// Returns true if direction is blocked from loc +// Checks if doors are open +/proc/DirBlocked(turf/loc,var/dir) + for(var/obj/structure/window/D in loc) + if(!D.density) + continue + if(D.fulltile) + return 1 + if(D.dir == dir) + return 1 + + for(var/obj/machinery/door/D in loc) + if(!D.density)//if the door is open + continue + else return 1 // if closed, it's a real, air blocking door + return 0 + +///////////////////////////////////////////////////////////////////////// + +/proc/getline(atom/M,atom/N)//Ultra-Fast Bresenham Line-Drawing Algorithm + var/px=M.x //starting x + var/py=M.y + var/line[] = list(locate(px,py,M.z)) + var/dx=N.x-px //x distance + var/dy=N.y-py + var/dxabs=abs(dx)//Absolute value of x distance + var/dyabs=abs(dy) + var/sdx=SIGN(dx) //Sign of x distance (+ or -) + var/sdy=SIGN(dy) + var/x=dxabs>>1 //Counters for steps taken, setting to distance/2 + var/y=dyabs>>1 //Bit-shifting makes me l33t. It also makes getline() unnessecarrily fast. + var/j //Generic integer for counting + if(dxabs>=dyabs) //x distance is greater than y + for(j=0;j=dxabs) //Every dyabs steps, step once in y direction + y-=dxabs + py+=sdy + px+=sdx //Step on in x direction + line+=locate(px,py,M.z)//Add the turf to the list + else + for(j=0;j=dyabs) + x-=dyabs + px+=sdx + py+=sdy + line+=locate(px,py,M.z) + return line + +//Same as the thing below just for density and without support for atoms. +/proc/can_line(atom/source, atom/target, length = 5) + var/turf/current = get_turf(source) + var/turf/target_turf = get_turf(target) + var/steps = 0 + + while(current != target_turf) + if(steps > length) + return FALSE + if(!current) + return FALSE + if(current.density) + return FALSE + current = get_step_towards(current, target_turf) + steps++ + return TRUE + +//Returns whether or not a player is a guest using their ckey as an input +/proc/IsGuestKey(key) + if(findtext(key, "Guest-", 1, 7) != 1) //was findtextEx + return 0 + + var/i, ch, len = length(key) + + for(i = 7, i <= len, ++i) + ch = text2ascii(key, i) + if(ch < 48 || ch > 57) + return 0 + return 1 + +//Ensure the frequency is within bounds of what it should be sending/recieving at +/proc/sanitize_frequency(var/f, var/low = PUBLIC_LOW_FREQ, var/high = PUBLIC_HIGH_FREQ) + f = round(f) + f = max(low, f) + f = min(high, f) + if((f % 2) == 0) //Ensure the last digit is an odd number + f += 1 + return f + +//Turns 1479 into 147.9 +/proc/format_frequency(var/f) + return "[round(f / 10)].[f % 10]" + +/obj/proc/atmosanalyzer_scan(var/datum/gas_mixture/air_contents, mob/user, var/obj/target = src) + var/obj/icon = target + user.visible_message("[user] has used the analyzer on [target].", "You use the analyzer on [target].") + var/pressure = air_contents.return_pressure() + var/total_moles = air_contents.total_moles() + + user.show_message("Results of analysis of [bicon(icon)] [target].", 1) + if(total_moles>0) + var/o2_concentration = air_contents.oxygen/total_moles + var/n2_concentration = air_contents.nitrogen/total_moles + var/co2_concentration = air_contents.carbon_dioxide/total_moles + var/plasma_concentration = air_contents.toxins/total_moles + + var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_concentration) + + user.show_message("Pressure: [round(pressure,0.1)] kPa", 1) + user.show_message("Nitrogen: [round(n2_concentration*100)] % ([round(air_contents.nitrogen,0.01)] moles)", 1) + user.show_message("Oxygen: [round(o2_concentration*100)] % ([round(air_contents.oxygen,0.01)] moles)", 1) + user.show_message("CO2: [round(co2_concentration*100)] % ([round(air_contents.carbon_dioxide,0.01)] moles)", 1) + user.show_message("Plasma: [round(plasma_concentration*100)] % ([round(air_contents.toxins,0.01)] moles)", 1) + if(unknown_concentration>0.01) + user.show_message("Unknown: [round(unknown_concentration*100)] % ([round(unknown_concentration*total_moles,0.01)] moles)", 1) + user.show_message("Total: [round(total_moles,0.01)] moles", 1) + user.show_message("Temperature: [round(air_contents.temperature-T0C)] °C", 1) + else + user.show_message("[target] is empty!", 1) + return + +//Picks a string of symbols to display as the law number for hacked or ion laws +/proc/ionnum() + return "[pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")]" + +//When an AI is activated, it can choose from a list of non-slaved borgs to have as a slave. +/proc/freeborg() + var/select = null + var/list/borgs = list() + for(var/mob/living/silicon/robot/A in GLOB.player_list) + if(A.stat == 2 || A.connected_ai || A.scrambledcodes || istype(A,/mob/living/silicon/robot/drone)) + continue + var/name = "[A.real_name] ([A.modtype] [A.braintype])" + borgs[name] = A + + if(borgs.len) + select = input("Unshackled borg signals detected:", "Borg selection", null, null) as null|anything in borgs + return borgs[select] + +//When a borg is activated, it can choose which AI it wants to be slaved to +/proc/active_ais() + . = list() + for(var/mob/living/silicon/ai/A in GLOB.living_mob_list) + if(A.stat == DEAD) + continue + if(A.control_disabled == 1) + continue + . += A + return . + +//Find an active ai with the least borgs. VERBOSE PROCNAME HUH! +/proc/select_active_ai_with_fewest_borgs() + var/mob/living/silicon/ai/selected + var/list/active = active_ais() + for(var/mob/living/silicon/ai/A in active) + if(!selected || (selected.connected_robots > A.connected_robots)) + selected = A + + return selected + +/proc/select_active_ai(var/mob/user) + var/list/ais = active_ais() + if(ais.len) + if(user) . = input(usr,"AI signals detected:", "AI selection") in ais + else . = pick(ais) + return . + +/proc/get_sorted_mobs() + var/list/old_list = getmobs() + var/list/AI_list = list() + var/list/Dead_list = list() + var/list/keyclient_list = list() + var/list/key_list = list() + var/list/logged_list = list() + for(var/named in old_list) + var/mob/M = old_list[named] + if(issilicon(M)) + AI_list |= M + else if(isobserver(M) || M.stat == DEAD) + Dead_list |= M + else if(M.key && M.client) + keyclient_list |= M + else if(M.key) + key_list |= M + else + logged_list |= M + old_list.Remove(named) + var/list/new_list = list() + new_list += AI_list + new_list += keyclient_list + new_list += key_list + new_list += logged_list + new_list += Dead_list + return new_list + +//Returns a list of all mobs with their name +/proc/getmobs() + + var/list/mobs = sortmobs() + var/list/names = list() + var/list/creatures = list() + var/list/namecounts = list() + for(var/mob/M in mobs) + var/name = M.name + if(name in names) + namecounts[name]++ + name = "[name] ([namecounts[name]])" + else + names.Add(name) + namecounts[name] = 1 + if(M.real_name && M.real_name != M.name) + name += " \[[M.real_name]\]" + if(M.stat == DEAD) + if(istype(M, /mob/dead/observer/)) + name += " \[ghost\]" + else + name += " \[dead\]" + creatures[name] = M + + return creatures + +//Orders mobs by type then by name +/proc/sortmobs() + var/list/moblist = list() + var/list/sortmob = sortAtom(GLOB.mob_list) + for(var/mob/living/silicon/ai/M in sortmob) + moblist.Add(M) + if(M.eyeobj) + moblist.Add(M.eyeobj) + for(var/mob/living/silicon/pai/M in sortmob) + moblist.Add(M) + for(var/mob/living/silicon/robot/M in sortmob) + moblist.Add(M) + for(var/mob/living/carbon/human/M in sortmob) + moblist.Add(M) + for(var/mob/living/carbon/brain/M in sortmob) + moblist.Add(M) + for(var/mob/living/carbon/alien/M in sortmob) + moblist.Add(M) + for(var/mob/dead/observer/M in sortmob) + moblist.Add(M) + for(var/mob/new_player/M in sortmob) + moblist.Add(M) + for(var/mob/living/carbon/slime/M in sortmob) + moblist.Add(M) + for(var/mob/living/simple_animal/M in sortmob) + moblist.Add(M) + return moblist + +// Format a power value in W, kW, MW, or GW. +/proc/DisplayPower(powerused) + if(powerused < 1000) //Less than a kW + return "[powerused] W" + else if(powerused < 1000000) //Less than a MW + return "[round((powerused * 0.001), 0.01)] kW" + else if(powerused < 1000000000) //Less than a GW + return "[round((powerused * 0.000001), 0.001)] MW" + return "[round((powerused * 0.000000001), 0.0001)] GW" + +//E = MC^2 +/proc/convert2energy(var/M) + var/E = M*(SPEED_OF_LIGHT_SQ) + return E + +//M = E/C^2 +/proc/convert2mass(var/E) + var/M = E/(SPEED_OF_LIGHT_SQ) + return M + +//Forces a variable to be posative +/proc/modulus(var/M) + if(M >= 0) + return M + if(M < 0) + return -M + +/proc/get_mob_by_ckey(key) + if(!key) + return + for(var/mob/M in GLOB.mob_list) + if(M.ckey == key) + return M + +// Returns the atom sitting on the turf. +// For example, using this on a disk, which is in a bag, on a mob, will return the mob because it's on the turf. +/proc/get_atom_on_turf(var/atom/movable/M) + var/atom/loc = M + while(loc && loc.loc && !istype(loc.loc, /turf/)) + loc = loc.loc + return loc + +/* +Returns 1 if the chain up to the area contains the given typepath +0 otherwise +*/ +/atom/proc/is_found_within(var/typepath) + var/atom/A = src + while(A.loc) + if(istype(A.loc, typepath)) + return 1 + A = A.loc + return 0 + +// the on-close client verb +// called when a browser popup window is closed after registering with proc/onclose() +// if a valid atom reference is supplied, call the atom's Topic() with "close=1" +// otherwise, just reset the client mob's machine var. + + +// returns the turf located at the map edge in the specified direction relative to A +// used for mass driver +/proc/get_edge_target_turf(var/atom/A, var/direction) + + var/turf/target = locate(A.x, A.y, A.z) + if(!A || !target) + return 0 + //since NORTHEAST == NORTH & EAST, etc, doing it this way allows for diagonal mass drivers in the future + //and isn't really any more complicated + + // Note diagonal directions won't usually be accurate + if(direction & NORTH) + target = locate(target.x, world.maxy, target.z) + if(direction & SOUTH) + target = locate(target.x, 1, target.z) + if(direction & EAST) + target = locate(world.maxx, target.y, target.z) + if(direction & WEST) + target = locate(1, target.y, target.z) + + return target + +// returns turf relative to A in given direction at set range +// result is bounded to map size +// note range is non-pythagorean +// used for disposal system +/proc/get_ranged_target_turf(var/atom/A, var/direction, var/range) + + var/x = A.x + var/y = A.y + if(direction & NORTH) + y = min(world.maxy, y + range) + if(direction & SOUTH) + y = max(1, y - range) + if(direction & EAST) + x = min(world.maxx, x + range) + if(direction & WEST) + x = max(1, x - range) + + return locate(x,y,A.z) + + +// returns turf relative to A offset in dx and dy tiles +// bound to map limits +/proc/get_offset_target_turf(var/atom/A, var/dx, var/dy) + var/x = min(world.maxx, max(1, A.x + dx)) + var/y = min(world.maxy, max(1, A.y + dy)) + return locate(x,y,A.z) + +//Makes sure MIDDLE is between LOW and HIGH. If not, it adjusts it. Returns the adjusted value. +/proc/between(var/low, var/middle, var/high) + return max(min(middle, high), low) + +proc/arctan(x) + var/y=arcsin(x/sqrt(1+x*x)) + return y + +//returns random gauss number +proc/GaussRand(var/sigma) + var/x,y,rsq + do + x=2*rand()-1 + y=2*rand()-1 + rsq=x*x+y*y + while(rsq>1 || !rsq) + return sigma*y*sqrt(-2*log(rsq)/rsq) + +//returns random gauss number, rounded to 'roundto' +proc/GaussRandRound(var/sigma,var/roundto) + return round(GaussRand(sigma),roundto) + +//Will return the contents of an atom recursivly to a depth of 'searchDepth' +/atom/proc/GetAllContents(searchDepth = 5) + var/list/toReturn = list() + + for(var/atom/part in contents) + toReturn += part + if(part.contents.len && searchDepth) + toReturn += part.GetAllContents(searchDepth - 1) + + return toReturn + +//Searches contents of the atom and returns the sum of all w_class of obj/item within +/atom/proc/GetTotalContentsWeight(searchDepth = 5) + var/weight = 0 + var/list/content = GetAllContents(searchDepth) + for(var/obj/item/I in content) + weight += I.w_class + return weight + +//Step-towards method of determining whether one atom can see another. Similar to viewers() +/proc/can_see(var/atom/source, var/atom/target, var/length=5) // I couldnt be arsed to do actual raycasting :I This is horribly inaccurate. + var/turf/current = get_turf(source) + var/turf/target_turf = get_turf(target) + var/steps = 1 + + if(current != target_turf) + current = get_step_towards(current, target_turf) + while(current != target_turf) + if(steps > length) + return 0 + if(current.opacity) + return 0 + for(var/thing in current) + var/atom/A = thing + if(A.opacity) + return 0 + current = get_step_towards(current, target_turf) + steps++ + + return 1 + +proc/is_blocked_turf(turf/T, exclude_mobs) + if(T.density) + return 1 + for(var/i in T) + var/atom/A = i + if(A.density && (!exclude_mobs || !ismob(A))) + return 1 + return 0 + +/proc/get_step_towards2(var/atom/ref , var/atom/trg) + var/base_dir = get_dir(ref, get_step_towards(ref,trg)) + var/turf/temp = get_step_towards(ref,trg) + + if(is_blocked_turf(temp)) + var/dir_alt1 = turn(base_dir, 90) + var/dir_alt2 = turn(base_dir, -90) + var/turf/turf_last1 = temp + var/turf/turf_last2 = temp + var/free_tile = null + var/breakpoint = 0 + + while(!free_tile && breakpoint < 10) + if(!is_blocked_turf(turf_last1)) + free_tile = turf_last1 + break + if(!is_blocked_turf(turf_last2)) + free_tile = turf_last2 + break + turf_last1 = get_step(turf_last1,dir_alt1) + turf_last2 = get_step(turf_last2,dir_alt2) + breakpoint++ + + if(!free_tile) return get_step(ref, base_dir) + else return get_step_towards(ref,free_tile) + + else return get_step(ref, base_dir) + +//Takes: Anything that could possibly have variables and a varname to check. +//Returns: 1 if found, 0 if not. +/proc/hasvar(var/datum/A, var/varname) + if(A.vars.Find(lowertext(varname))) return 1 + else return 0 + +//Returns: all the areas in the world +/proc/return_areas() + var/list/area/areas = list() + for(var/area/A in world) + areas += A + return areas + +//Returns: all the areas in the world, sorted. +/proc/return_sorted_areas() + return sortAtom(return_areas()) + +//Takes: Area type as text string or as typepath OR an instance of the area. +//Returns: A list of all areas of that type in the world. +/proc/get_areas(var/areatype) + if(!areatype) return null + if(istext(areatype)) areatype = text2path(areatype) + if(isarea(areatype)) + var/area/areatemp = areatype + areatype = areatemp.type + + var/list/areas = new/list() + for(var/area/N in world) + if(istype(N, areatype)) areas += N + return areas + +//Takes: Area type as text string or as typepath OR an instance of the area. +//Returns: A list of all turfs in areas of that type of that type in the world. +/proc/get_area_turfs(var/areatype) + if(!areatype) return null + if(istext(areatype)) areatype = text2path(areatype) + if(isarea(areatype)) + var/area/areatemp = areatype + areatype = areatemp.type + + var/list/turfs = new/list() + for(var/area/N in world) + if(istype(N, areatype)) + for(var/turf/T in N) turfs += T + return turfs + +//Takes: Area type as text string or as typepath OR an instance of the area. +//Returns: A list of all atoms (objs, turfs, mobs) in areas of that type of that type in the world. +/proc/get_area_all_atoms(var/areatype) + if(!areatype) return null + if(istext(areatype)) areatype = text2path(areatype) + if(isarea(areatype)) + var/area/areatemp = areatype + areatype = areatemp.type + + var/list/atoms = new/list() + for(var/area/N in world) + if(istype(N, areatype)) + for(var/atom/A in N) + atoms += A + return atoms + +/datum/coords //Simple datum for storing coordinates. + var/x_pos = null + var/y_pos = null + var/z_pos = null + +/area/proc/move_contents_to(var/area/A, var/turftoleave=null, var/direction = null) + //Takes: Area. Optional: turf type to leave behind. + //Returns: Nothing. + //Notes: Attempts to move the contents of one area to another area. + // Movement based on lower left corner. Tiles that do not fit + // into the new area will not be moved. + + if(!A || !src) return 0 + + var/list/turfs_src = get_area_turfs(src.type) + var/list/turfs_trg = get_area_turfs(A.type) + + var/src_min_x = 0 + var/src_min_y = 0 + for(var/turf/T in turfs_src) + if(T.x < src_min_x || !src_min_x) src_min_x = T.x + if(T.y < src_min_y || !src_min_y) src_min_y = T.y + + var/trg_min_x = 0 + var/trg_min_y = 0 + for(var/turf/T in turfs_trg) + if(T.x < trg_min_x || !trg_min_x) trg_min_x = T.x + if(T.y < trg_min_y || !trg_min_y) trg_min_y = T.y + + var/list/refined_src = new/list() + for(var/turf/T in turfs_src) + refined_src += T + refined_src[T] = new/datum/coords + var/datum/coords/C = refined_src[T] + C.x_pos = (T.x - src_min_x) + C.y_pos = (T.y - src_min_y) + + var/list/refined_trg = new/list() + for(var/turf/T in turfs_trg) + refined_trg += T + refined_trg[T] = new/datum/coords + var/datum/coords/C = refined_trg[T] + C.x_pos = (T.x - trg_min_x) + C.y_pos = (T.y - trg_min_y) + + var/list/fromupdate = new/list() + var/list/toupdate = new/list() + + moving: + for(var/turf/T in refined_src) + var/datum/coords/C_src = refined_src[T] + for(var/turf/B in refined_trg) + var/datum/coords/C_trg = refined_trg[B] + if(C_src.x_pos == C_trg.x_pos && C_src.y_pos == C_trg.y_pos) + + var/old_dir1 = T.dir + var/old_icon_state1 = T.icon_state + var/old_icon1 = T.icon + + var/turf/X = B.ChangeTurf(T.type) + X.dir = old_dir1 + X.icon_state = old_icon_state1 + X.icon = old_icon1 //Shuttle floors are in shuttle.dmi while the defaults are floors.dmi + + // Give the new turf our air, if simulated + if(istype(X, /turf/simulated) && istype(T, /turf/simulated)) + var/turf/simulated/sim = X + sim.copy_air_with_tile(T) + + + /* Quick visual fix for some weird shuttle corner artefacts when on transit space tiles */ + if(direction && findtext(X.icon_state, "swall_s")) + + // Spawn a new shuttle corner object + var/obj/corner = new() + corner.loc = X + corner.density = 1 + corner.anchored = 1 + corner.icon = X.icon + corner.icon_state = replacetext(X.icon_state, "_s", "_f") + corner.tag = "delete me" + corner.name = "wall" + + // Find a new turf to take on the property of + var/turf/nextturf = get_step(corner, direction) + if(!nextturf || !istype(nextturf, /turf/space)) + nextturf = get_step(corner, turn(direction, 180)) + + + // Take on the icon of a neighboring scrolling space icon + X.icon = nextturf.icon + X.icon_state = nextturf.icon_state + + + for(var/obj/O in T) + + // Reset the shuttle corners + if(O.tag == "delete me") + X.icon = 'icons/turf/shuttle.dmi' + X.icon_state = replacetext(O.icon_state, "_f", "_s") // revert the turf to the old icon_state + X.name = "wall" + qdel(O) // prevents multiple shuttle corners from stacking + continue + if(!istype(O,/obj)) continue + O.loc.Exited(O) + O.setLoc(X,teleported=1) + O.loc.Entered(O) + for(var/mob/M in T) + if(!M.move_on_shuttle) + continue + M.loc = X + +// var/area/AR = X.loc + +// if(AR.lighting_use_dynamic) //TODO: rewrite this code so it's not messed by lighting ~Carn +// X.opacity = !X.opacity +// X.set_opacity(!X.opacity) + + toupdate += X + + if(turftoleave) + fromupdate += T.ChangeTurf(turftoleave) + else + T.ChangeTurf(T.baseturf) + + refined_src -= T + refined_trg -= B + continue moving + + if(toupdate.len) + for(var/turf/simulated/T1 in toupdate) + SSair.remove_from_active(T1) + T1.CalculateAdjacentTurfs() + SSair.add_to_active(T1,1) + + if(fromupdate.len) + for(var/turf/simulated/T2 in fromupdate) + SSair.remove_from_active(T2) + T2.CalculateAdjacentTurfs() + SSair.add_to_active(T2,1) + + + + +/proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0, var/atom/newloc = null) + if(!original) + return null + + var/obj/O = null + + if(sameloc) + O=new original.type(original.loc) + else + O=new original.type(newloc) + + if(perfectcopy) + if((O) && (original)) + var/static/list/forbidden_vars = list("type","loc","locs","vars", "parent","parent_type", "verbs","ckey","key","power_supply","contents","reagents","stat","x","y","z","group") + + for(var/V in original.vars - forbidden_vars) + if(istype(original.vars[V],/list)) + var/list/L = original.vars[V] + O.vars[V] = L.Copy() + else if(istype(original.vars[V],/datum)) + continue // this would reference the original's object, that will break when it is used or deleted. + else + O.vars[V] = original.vars[V] + if(istype(O)) + O.update_icon() + return O + +/area/proc/copy_contents_to(var/area/A , var/platingRequired = 0 ) + //Takes: Area. Optional: If it should copy to areas that don't have plating + //Returns: Nothing. + //Notes: Attempts to move the contents of one area to another area. + // Movement based on lower left corner. Tiles that do not fit + // into the new area will not be moved. + + if(!A || !src) return 0 + + var/list/turfs_src = get_area_turfs(src.type) + var/list/turfs_trg = get_area_turfs(A.type) + + var/src_min_x = 0 + var/src_min_y = 0 + for(var/turf/T in turfs_src) + if(T.x < src_min_x || !src_min_x) src_min_x = T.x + if(T.y < src_min_y || !src_min_y) src_min_y = T.y + + var/trg_min_x = 0 + var/trg_min_y = 0 + for(var/turf/T in turfs_trg) + if(T.x < trg_min_x || !trg_min_x) trg_min_x = T.x + if(T.y < trg_min_y || !trg_min_y) trg_min_y = T.y + + var/list/refined_src = new/list() + for(var/turf/T in turfs_src) + refined_src += T + refined_src[T] = new/datum/coords + var/datum/coords/C = refined_src[T] + C.x_pos = (T.x - src_min_x) + C.y_pos = (T.y - src_min_y) + + var/list/refined_trg = new/list() + for(var/turf/T in turfs_trg) + refined_trg += T + refined_trg[T] = new/datum/coords + var/datum/coords/C = refined_trg[T] + C.x_pos = (T.x - trg_min_x) + C.y_pos = (T.y - trg_min_y) + + var/list/toupdate = new/list() + + var/copiedobjs = list() + + + moving: + for(var/turf/T in refined_src) + var/datum/coords/C_src = refined_src[T] + for(var/turf/B in refined_trg) + var/datum/coords/C_trg = refined_trg[B] + if(C_src.x_pos == C_trg.x_pos && C_src.y_pos == C_trg.y_pos) + + var/old_dir1 = T.dir + var/old_icon_state1 = T.icon_state + var/old_icon1 = T.icon + + if(platingRequired) + if(istype(B, /turf/space)) + continue moving + + var/turf/X = new T.type(B) + X.dir = old_dir1 + X.icon_state = old_icon_state1 + X.icon = old_icon1 //Shuttle floors are in shuttle.dmi while the defaults are floors.dmi + + + var/list/objs = new/list() + var/list/newobjs = new/list() + var/list/mobs = new/list() + var/list/newmobs = new/list() + + for(var/obj/O in T) + + if(!istype(O,/obj)) + continue + + objs += O + + + for(var/obj/O in objs) + newobjs += DuplicateObject(O , 1) + + + for(var/obj/O in newobjs) + O.loc = X + + for(var/mob/M in T) + + if(!M.move_on_shuttle) + continue + mobs += M + + for(var/mob/M in mobs) + newmobs += DuplicateObject(M , 1) + + for(var/mob/M in newmobs) + M.loc = X + + copiedobjs += newobjs + copiedobjs += newmobs + + + + for(var/V in T.vars) + if(!(V in list("type","loc","locs","vars", "parent", "parent_type","verbs","ckey","key","x","y","z","contents", "luminosity", "group"))) + X.vars[V] = T.vars[V] + +// var/area/AR = X.loc + +// if(AR.lighting_use_dynamic) +// X.opacity = !X.opacity +// X.sd_set_opacity(!X.opacity) //TODO: rewrite this code so it's not messed by lighting ~Carn + + toupdate += X + + refined_src -= T + refined_trg -= B + continue moving + + + + if(toupdate.len) + for(var/turf/simulated/T1 in toupdate) + T1.CalculateAdjacentTurfs() + SSair.add_to_active(T1,1) + + + return copiedobjs + + + +proc/get_cardinal_dir(atom/A, atom/B) + var/dx = abs(B.x - A.x) + var/dy = abs(B.y - A.y) + return get_dir(A, B) & (rand() * (dx+dy) < dy ? 3 : 12) + +//chances are 1:value. anyprob(1) will always return true +proc/anyprob(value) + return (rand(1,value)==value) + +proc/view_or_range(distance = world.view , center = usr , type) + switch(type) + if("view") + . = view(distance,center) + if("range") + . = range(distance,center) + return + +proc/oview_or_orange(distance = world.view , center = usr , type) + switch(type) + if("view") + . = oview(distance,center) + if("range") + . = orange(distance,center) + return + +proc/get_mob_with_client_list() + var/list/mobs = list() + for(var/mob/M in GLOB.mob_list) + if(M.client) + mobs += M + return mobs + + +/proc/parse_zone(zone) + if(zone == "r_hand") return "right hand" + else if(zone == "l_hand") return "left hand" + else if(zone == "l_arm") return "left arm" + else if(zone == "r_arm") return "right arm" + else if(zone == "l_leg") return "left leg" + else if(zone == "r_leg") return "right leg" + else if(zone == "l_foot") return "left foot" + else if(zone == "r_foot") return "right foot" + else if(zone == "l_hand") return "left hand" + else if(zone == "r_hand") return "right hand" + else if(zone == "l_foot") return "left foot" + else if(zone == "r_foot") return "right foot" + else return zone + +/* + + Gets the turf this atom's *ICON* appears to inhabit + It takes into account: + * Pixel_x/y + * Matrix x/y + + NOTE: if your atom has non-standard bounds then this proc + will handle it, but: + * if the bounds are even, then there are an even amount of "middle" turfs, the one to the EAST, NORTH, or BOTH is picked + (this may seem bad, but you're atleast as close to the center of the atom as possible, better than byond's default loc being all the way off) + * if the bounds are odd, the true middle turf of the atom is returned + +*/ + +/proc/get_turf_pixel(atom/movable/AM) + if(!istype(AM)) + return + + //Find AM's matrix so we can use it's X/Y pixel shifts + var/matrix/M = matrix(AM.transform) + + var/pixel_x_offset = AM.pixel_x + M.get_x_shift() + var/pixel_y_offset = AM.pixel_y + M.get_y_shift() + + //Irregular objects + if(AM.bound_height != world.icon_size || AM.bound_width != world.icon_size) + var/icon/AMicon = icon(AM.icon, AM.icon_state) + pixel_x_offset += ((AMicon.Width()/world.icon_size)-1)*(world.icon_size*0.5) + pixel_y_offset += ((AMicon.Height()/world.icon_size)-1)*(world.icon_size*0.5) + qdel(AMicon) + + //DY and DX + var/rough_x = round(round(pixel_x_offset,world.icon_size)/world.icon_size) + var/rough_y = round(round(pixel_y_offset,world.icon_size)/world.icon_size) + + //Find coordinates + var/turf/T = get_turf(AM) //use AM's turfs, as it's coords are the same as AM's AND AM's coords are lost if it is inside another atom + if(!T) + return null + var/final_x = T.x + rough_x + var/final_y = T.y + rough_y + + if(final_x || final_y) + return locate(final_x, final_y, T.z) + +//Finds the distance between two atoms, in pixels +//centered = 0 counts from turf edge to edge +//centered = 1 counts from turf center to turf center +//of course mathematically this is just adding world.icon_size on again +/proc/getPixelDistance(var/atom/A, var/atom/B, var/centered = 1) + if(!istype(A)||!istype(B)) + return 0 + . = bounds_dist(A, B) + sqrt((((A.pixel_x+B.pixel_x)**2) + ((A.pixel_y+B.pixel_y)**2))) + if(centered) + . += world.icon_size + +/proc/get(atom/loc, type) + while(loc) + if(istype(loc, type)) + return loc + loc = loc.loc + return null + +/proc/get_turf_or_move(turf/location) + return get_turf(location) + + +//For objects that should embed, but make no sense being is_sharp or is_pointed() +//e.g: rods +var/list/can_embed_types = typecacheof(list( + /obj/item/stack/rods, + /obj/item/pipe)) + +/proc/can_embed(obj/item/W) + if(is_sharp(W)) + return 1 + if(is_pointed(W)) + return 1 + + if(is_type_in_typecache(W, can_embed_types)) + return 1 + +//Quick type checks for some tools +var/global/list/common_tools = list( +/obj/item/stack/cable_coil, +/obj/item/wrench, +/obj/item/weldingtool, +/obj/item/screwdriver, +/obj/item/wirecutters, +/obj/item/multitool, +/obj/item/crowbar) + +/proc/istool(O) + if(O && is_type_in_list(O, common_tools)) + return 1 + return 0 + +/proc/iswrench(O) + if(istype(O, /obj/item/wrench)) + return 1 + return 0 + +/proc/iswelder(O) + if(istype(O, /obj/item/weldingtool)) + return 1 + return 0 + +/proc/iscoil(O) + if(istype(O, /obj/item/stack/cable_coil)) + return 1 + return 0 + +/proc/iswirecutter(O) + if(istype(O, /obj/item/wirecutters)) + return 1 + return 0 + +/proc/isscrewdriver(O) + if(istype(O, /obj/item/screwdriver)) + return 1 + return 0 + +/proc/ismultitool(O) + if(istype(O, /obj/item/multitool)) + return 1 + return 0 + +/proc/iscrowbar(O) + if(istype(O, /obj/item/crowbar)) + return 1 + return 0 + +/proc/ispowertool(O)//used to check if a tool can force powered doors + if(istype(O, /obj/item/crowbar/power) || istype(O, /obj/item/mecha_parts/mecha_equipment/medical/rescue_jaw)) + return TRUE + return FALSE + +/proc/iswire(O) + if(istype(O, /obj/item/stack/cable_coil)) + return 1 + return 0 + +/proc/is_hot(obj/item/W as obj) + if(istype(W, /obj/item/weldingtool)) + var/obj/item/weldingtool/O = W + if(O.isOn()) + return 2500 + else + return 0 + if(istype(W, /obj/item/lighter)) + var/obj/item/lighter/O = W + if(O.lit) + return 1500 + else + return 0 + if(istype(W, /obj/item/match)) + var/obj/item/match/O = W + if(O.lit == 1) + return 1000 + else + return 0 + if(istype(W, /obj/item/clothing/mask/cigarette)) + var/obj/item/clothing/mask/cigarette/O = W + if(O.lit) + return 1000 + else + return 0 + if(istype(W, /obj/item/candle)) + var/obj/item/candle/O = W + if(O.lit) + return 1000 + else + return 0 + if(istype(W, /obj/item/flashlight/flare)) + var/obj/item/flashlight/flare/O = W + if(O.on) + return 1000 + else + return 0 + if(istype(W, /obj/item/gun/energy/plasmacutter)) + return 3800 + if(istype(W, /obj/item/melee/energy)) + var/obj/item/melee/energy/O = W + if(O.active) + return 3500 + else + return 0 + if(istype(W, /obj/item/assembly/igniter)) + return 20000 + else + return 0 + +//Whether or not the given item counts as sharp in terms of dealing damage +/proc/is_sharp(obj/O) + if(!O) + return 0 + if(O.sharp) + return 1 + return 0 + +/proc/is_surgery_tool(obj/item/W as obj) + return ( \ + istype(W, /obj/item/scalpel) || \ + istype(W, /obj/item/hemostat) || \ + istype(W, /obj/item/retractor) || \ + istype(W, /obj/item/cautery) || \ + istype(W, /obj/item/bonegel) || \ + istype(W, /obj/item/bonesetter) + ) + +/proc/reverse_direction(var/dir) + switch(dir) + if(NORTH) + return SOUTH + if(NORTHEAST) + return SOUTHWEST + if(EAST) + return WEST + if(SOUTHEAST) + return NORTHWEST + if(SOUTH) + return NORTH + if(SOUTHWEST) + return NORTHEAST + if(WEST) + return EAST + if(NORTHWEST) + return SOUTHEAST + +/* +Checks if that loc and dir has a item on the wall +*/ +var/list/static/global/wall_items = typecacheof(list(/obj/machinery/power/apc, /obj/machinery/alarm, + /obj/item/radio/intercom, /obj/structure/extinguisher_cabinet, /obj/structure/reagent_dispensers/peppertank, + /obj/machinery/status_display, /obj/machinery/requests_console, /obj/machinery/light_switch, /obj/structure/sign, + /obj/machinery/newscaster, /obj/machinery/firealarm, /obj/structure/noticeboard, /obj/machinery/door_control, + /obj/machinery/computer/security/telescreen, /obj/machinery/embedded_controller/radio/airlock, + /obj/item/storage/secure/safe, /obj/machinery/door_timer, /obj/machinery/flasher, /obj/machinery/keycard_auth, + /obj/structure/mirror, /obj/structure/closet/fireaxecabinet, /obj/machinery/computer/security/telescreen/entertainment, + /obj/structure/sign)) + +/proc/gotwallitem(loc, dir) + for(var/obj/O in loc) + if(is_type_in_typecache(O, wall_items)) + //Direction works sometimes + if(O.dir == dir) + return 1 + + //Some stuff doesn't use dir properly, so we need to check pixel instead + switch(dir) + if(SOUTH) + if(O.pixel_y > 10) + return 1 + if(NORTH) + if(O.pixel_y < -10) + return 1 + if(WEST) + if(O.pixel_x > 10) + return 1 + if(EAST) + if(O.pixel_x < -10) + return 1 + + //Some stuff is placed directly on the wallturf (signs) + for(var/obj/O in get_step(loc, dir)) + if(is_type_in_typecache(O, wall_items)) + if(abs(O.pixel_x) <= 10 && abs(O.pixel_y) <= 10) + return 1 + return 0 + + +proc/get_angle(atom/a, atom/b) + return atan2(b.y - a.y, b.x - a.x) + +proc/atan2(x, y) + if(!x && !y) return 0 + return y >= 0 ? arccos(x / sqrt(x * x + y * y)) : -arccos(x / sqrt(x * x + y * y)) + +/proc/format_text(text) + return replacetext(replacetext(text,"\proper ",""),"\improper ","") + +/* +Standard way to write links -Sayu +*/ + +/proc/topic_link(var/datum/D, var/arglist, var/content) + if(istype(arglist,/list)) + arglist = list2params(arglist) + return "[content]" + + + +/proc/get_location_accessible(mob/M, location) + var/covered_locations = 0 //based on body_parts_covered + var/face_covered = 0 //based on flags_inv + var/eyesmouth_covered = 0 //based on flags_cover + if(iscarbon(M)) + var/mob/living/carbon/C = M + for(var/obj/item/clothing/I in list(C.back, C.wear_mask)) + covered_locations |= I.body_parts_covered + face_covered |= I.flags_inv + eyesmouth_covered |= I.flags_cover + if(ishuman(C)) + var/mob/living/carbon/human/H = C + for(var/obj/item/I in list(H.wear_suit, H.w_uniform, H.shoes, H.belt, H.gloves, H.glasses, H.head, H.r_ear, H.l_ear)) + covered_locations |= I.body_parts_covered + face_covered |= I.flags_inv + eyesmouth_covered |= I.flags_cover + + switch(location) + if("head") + if(covered_locations & HEAD) + return 0 + if("eyes") + if(face_covered & HIDEEYES || eyesmouth_covered & GLASSESCOVERSEYES || eyesmouth_covered & HEADCOVERSEYES) + return 0 + if("mouth") + if(covered_locations & HEAD || face_covered & HIDEFACE || eyesmouth_covered & MASKCOVERSMOUTH) + return 0 + if("chest") + if(covered_locations & UPPER_TORSO) + return 0 + if("groin") + if(covered_locations & LOWER_TORSO) + return 0 + if("l_arm") + if(covered_locations & ARM_LEFT) + return 0 + if("r_arm") + if(covered_locations & ARM_RIGHT) + return 0 + if("l_leg") + if(covered_locations & LEG_LEFT) + return 0 + if("r_leg") + if(covered_locations & LEG_RIGHT) + return 0 + if("l_hand") + if(covered_locations & HAND_LEFT) + return 0 + if("r_hand") + if(covered_locations & HAND_RIGHT) + return 0 + if("l_foot") + if(covered_locations & FOOT_LEFT) + return 0 + if("r_foot") + if(covered_locations & FOOT_RIGHT) + return 0 + + return 1 + +/proc/check_target_facings(mob/living/initator, mob/living/target) + /*This can be used to add additional effects on interactions between mobs depending on how the mobs are facing each other, such as adding a crit damage to blows to the back of a guy's head. + Given how click code currently works (Nov '13), the initiating mob will be facing the target mob most of the time + That said, this proc should not be used if the change facing proc of the click code is overriden at the same time*/ + if(!ismob(target) || target.lying) + //Make sure we are not doing this for things that can't have a logical direction to the players given that the target would be on their side + return FACING_FAILED + if(initator.dir == target.dir) //mobs are facing the same direction + return FACING_SAME_DIR + if(is_A_facing_B(initator, target) && is_A_facing_B(target, initator)) //mobs are facing each other + return FACING_EACHOTHER + if(initator.dir + 2 == target.dir || initator.dir - 2 == target.dir || initator.dir + 6 == target.dir || initator.dir - 6 == target.dir) //Initating mob is looking at the target, while the target mob is looking in a direction perpendicular to the 1st + return FACING_INIT_FACING_TARGET_TARGET_FACING_PERPENDICULAR + + +atom/proc/GetTypeInAllContents(typepath) + var/list/processing_list = list(src) + var/list/processed = list() + + var/atom/found = null + + while(processing_list.len && found==null) + var/atom/A = processing_list[1] + if(istype(A, typepath)) + found = A + + processing_list -= A + + for(var/atom/a in A) + if(!(a in processed)) + processing_list |= a + + processed |= A + + return found + +/proc/random_step(atom/movable/AM, steps, chance) + var/initial_chance = chance + while(steps > 0) + if(prob(chance)) + step(AM, pick(alldirs)) + chance = max(chance - (initial_chance / steps), 0) + steps-- + +/proc/get_random_colour(var/simple, var/lower, var/upper) + var/colour + if(simple) + colour = pick(list("FF0000","FF7F00","FFFF00","00FF00","0000FF","4B0082","8F00FF")) + else + for(var/i=1;i<=3;i++) + var/temp_col = "[num2hex(rand(lower,upper))]" + if(length(temp_col )<2) + temp_col = "0[temp_col]" + colour += temp_col + return colour + +/proc/get_distant_turf(var/turf/T,var/direction,var/distance) + if(!T || !direction || !distance) return + + var/dest_x = T.x + var/dest_y = T.y + var/dest_z = T.z + + if(direction & NORTH) + dest_y = min(world.maxy, dest_y+distance) + if(direction & SOUTH) + dest_y = max(0, dest_y-distance) + if(direction & EAST) + dest_x = min(world.maxy, dest_x+distance) + if(direction & WEST) + dest_x = max(0, dest_x-distance) + + return locate(dest_x,dest_y,dest_z) + +var/mob/dview/dview_mob = new + +//Version of view() which ignores darkness, because BYOND doesn't have it. +/proc/dview(var/range = world.view, var/center, var/invis_flags = 0) + if(!center) + return + + dview_mob.loc = center + + dview_mob.see_invisible = invis_flags + + . = view(range, dview_mob) + dview_mob.loc = null + +/mob/dview + invisibility = 101 + density = 0 + move_force = 0 + pull_force = 0 + move_resist = INFINITY + simulated = 0 + canmove = FALSE + see_in_dark = 1e6 + +/mob/dview/New() //For whatever reason, if this isn't called, then BYOND will throw a type mismatch runtime when attempting to add this to the mobs list. -Fox + +/mob/dview/Destroy() + // should never be deleted + return QDEL_HINT_LETMELIVE + +/proc/IsValidSrc(A) + if(istype(A, /datum)) + var/datum/D = A + return !QDELETED(D) + if(istype(A, /client)) + return TRUE + return FALSE + +//can a window be here, or is there a window blocking it? +/proc/valid_window_location(turf/T, dir_to_check) + if(!T) + return FALSE + for(var/obj/O in T) + if(istype(O, /obj/machinery/door/window) && (O.dir == dir_to_check || dir_to_check == FULLTILE_WINDOW_DIR)) + return FALSE + if(istype(O, /obj/structure/windoor_assembly)) + var/obj/structure/windoor_assembly/W = O + if(W.ini_dir == dir_to_check || dir_to_check == FULLTILE_WINDOW_DIR) + return FALSE + if(istype(O, /obj/structure/window)) + var/obj/structure/window/W = O + if(W.ini_dir == dir_to_check || W.ini_dir == FULLTILE_WINDOW_DIR || dir_to_check == FULLTILE_WINDOW_DIR) + return FALSE + return TRUE + +//Get the dir to the RIGHT of dir if they were on a clock +//NORTH --> NORTHEAST +/proc/get_clockwise_dir(dir) + . = angle2dir(dir2angle(dir)+45) + +//Get the dir to the LEFT of dir if they were on a clock +//NORTH --> NORTHWEST +/proc/get_anticlockwise_dir(dir) + . = angle2dir(dir2angle(dir)-45) + + +//Compare A's dir, the clockwise dir of A and the anticlockwise dir of A +//To the opposite dir of the dir returned by get_dir(B,A) +//If one of them is a match, then A is facing B +/proc/is_A_facing_B(atom/A, atom/B) + if(!istype(A) || !istype(B)) + return 0 + if(isliving(A)) + var/mob/living/LA = A + if(LA.lying) + return 0 + var/goal_dir = angle2dir(dir2angle(get_dir(B, A)+180)) + var/clockwise_A_dir = get_clockwise_dir(A.dir) + var/anticlockwise_A_dir = get_anticlockwise_dir(B.dir) + + if(A.dir == goal_dir || clockwise_A_dir == goal_dir || anticlockwise_A_dir == goal_dir) + return 1 + return 0 + +//This is just so you can stop an orbit. +//orbit() can run without it (swap orbiting for A) +//but then you can never stop it and that's just silly. +/atom/movable/var/atom/orbiting = null + +//A: atom to orbit +//radius: range to orbit at, radius of the circle formed by orbiting +//clockwise: whether you orbit clockwise or anti clockwise +//rotation_speed: how fast to rotate +//rotation_segments: the resolution of the orbit circle, less = a more block circle, this can be used to produce hexagons (6 segments) triangles (3 segments), and so on, 36 is the best default. +//pre_rotation: Chooses to rotate src 90 degress towards the orbit dir (clockwise/anticlockwise), useful for things to go "head first" like ghosts +//lockinorbit: Forces src to always be on A's turf, otherwise the orbit cancels when src gets too far away (eg: ghosts) + +/atom/movable/proc/orbit(atom/A, radius = 10, clockwise = FALSE, rotation_speed = 20, rotation_segments = 36, pre_rotation = TRUE, lockinorbit = FALSE, forceMove = FALSE) + if(!istype(A)) + return + + if(orbiting) + stop_orbit() + + orbiting = A + var/matrix/initial_transform = matrix(transform) + var/lastloc = loc + + //Head first! + if(pre_rotation) + var/matrix/M = matrix(transform) + var/pre_rot = 90 + if(!clockwise) + pre_rot = -90 + M.Turn(pre_rot) + transform = M + + var/matrix/shift = matrix(transform) + shift.Translate(0,radius) + transform = shift + + SpinAnimation(rotation_speed, -1, clockwise, rotation_segments) + + //we stack the orbits up client side, so we can assign this back to normal server side without it breaking the orbit + transform = initial_transform + while(orbiting && orbiting == A && A.loc) + var/targetloc = get_turf(A) + if(!lockinorbit && loc != lastloc && loc != targetloc) + break + if(forceMove) + forceMove(targetloc) + else + loc = targetloc + lastloc = loc + sleep(0.6) + + if(orbiting == A) //make sure we haven't started orbiting something else. + orbiting = null + SpinAnimation(0,0) + + + +/atom/movable/proc/stop_orbit() + orbiting = null + +//Centers an image. +//Requires: +//The Image +//The x dimension of the icon file used in the image +//The y dimension of the icon file used in the image +// eg: center_image(I, 32,32) +// eg2: center_image(I, 96,96) +/proc/center_image(var/image/I, x_dimension = 0, y_dimension = 0) + if(!I) + return + + if(!x_dimension || !y_dimension) + return + + //Get out of here, punk ass kids calling procs needlessly + if((x_dimension == world.icon_size) && (y_dimension == world.icon_size)) + return I + + //Offset the image so that it's bottom left corner is shifted this many pixels + //This makes it infinitely easier to draw larger inhands/images larger than world.iconsize + //but still use them in game + var/x_offset = -((x_dimension/world.icon_size)-1)*(world.icon_size*0.5) + var/y_offset = -((y_dimension/world.icon_size)-1)*(world.icon_size*0.5) + + //Correct values under world.icon_size + if(x_dimension < world.icon_size) + x_offset *= -1 + if(y_dimension < world.icon_size) + y_offset *= -1 + + I.pixel_x = x_offset + I.pixel_y = y_offset + + return I + +//similar function to RANGE_TURFS(), but will search spiralling outwards from the center (like the above, but only turfs) +/proc/spiral_range_turfs(dist=0, center=usr, orange=0) + if(!dist) + if(!orange) + return list(center) + else + return list() + + var/turf/t_center = get_turf(center) + if(!t_center) + return list() + + var/list/L = list() + var/turf/T + var/y + var/x + var/c_dist = 1 + + if(!orange) + L += t_center + + while( c_dist <= dist ) + y = t_center.y + c_dist + x = t_center.x - c_dist + 1 + for(x in x to t_center.x+c_dist) + T = locate(x,y,t_center.z) + if(T) + L += T + + y = t_center.y + c_dist - 1 + x = t_center.x + c_dist + for(y in t_center.y-c_dist to y) + T = locate(x,y,t_center.z) + if(T) + L += T + + y = t_center.y - c_dist + x = t_center.x + c_dist - 1 + for(x in t_center.x-c_dist to x) + T = locate(x,y,t_center.z) + if(T) + L += T + + y = t_center.y - c_dist + 1 + x = t_center.x - c_dist + for(y in y to t_center.y+c_dist) + T = locate(x,y,t_center.z) + if(T) + L += T + c_dist++ + + return L + +//ultra range (no limitations on distance, faster than range for distances > 8); including areas drastically decreases performance +/proc/urange(dist=0, atom/center=usr, orange=0, areas=0) + if(!dist) + if(!orange) + return list(center) + else + return list() + + var/list/turfs = RANGE_TURFS(dist, center) + if(orange) + turfs -= get_turf(center) + . = list() + for(var/V in turfs) + var/turf/T = V + . += T + . += T.contents + if(areas) + . |= T.loc + +/proc/turf_clear(turf/T) + for(var/atom/A in T) + if(A.simulated) + return FALSE + return TRUE + +/proc/screen_loc2turf(scr_loc, turf/origin) + var/tX = splittext(scr_loc, ",") + var/tY = splittext(tX[2], ":") + var/tZ = origin.z + tY = tY[1] + tX = splittext(tX[1], ":") + tX = tX[1] + tX = max(1, min(world.maxx, origin.x + (text2num(tX) - (world.view + 1)))) + tY = max(1, min(world.maxy, origin.y + (text2num(tY) - (world.view + 1)))) + return locate(tX, tY, tZ) + +/proc/get_closest_atom(type, list, source) + var/closest_atom + var/closest_distance + for(var/A in list) + if(!istype(A, type)) + continue + var/distance = get_dist(source, A) + if(!closest_distance) + closest_distance = distance + closest_atom = A + else + if(closest_distance > distance) + closest_distance = distance + closest_atom = A + return closest_atom + +/proc/pick_closest_path(value, list/matches = get_fancy_list_of_atom_types()) + if(value == FALSE) //nothing should be calling us with a number, so this is safe + value = input("Enter type to find (blank for all, cancel to cancel)", "Search for type") as null|text + if(isnull(value)) + return + value = trim(value) + if(!isnull(value) && value != "") + matches = filter_fancy_list(matches, value) + + if(matches.len == 0) + return + + var/chosen + if(matches.len == 1) + chosen = matches[1] + else + chosen = input("Select a type", "Pick Type", matches[1]) as null|anything in matches + if(!chosen) + return + chosen = matches[chosen] + return chosen + +/proc/make_types_fancy(var/list/types) + if(ispath(types)) + types = list(types) + . = list() + for(var/type in types) + var/typename = "[type]" + var/static/list/TYPES_SHORTCUTS = list( + //longest paths comes first - otherwise they get shadowed by the more generic ones + /obj/effect/decal/cleanable = "CLEANABLE", + /obj/effect = "EFFECT", + /obj/item/ammo_casing = "AMMO", + /obj/item/book/manual = "MANUAL", + /obj/item/borg/upgrade = "BORG_UPGRADE", + /obj/item/cartridge = "PDA_CART", + /obj/item/clothing/head/helmet/space = "SPESSHELMET", + /obj/item/clothing/head = "HEAD", + /obj/item/clothing/under = "UNIFORM", + /obj/item/clothing/shoes = "SHOES", + /obj/item/clothing/suit = "SUIT", + /obj/item/clothing/gloves = "GLOVES", + /obj/item/clothing/mask/cigarette = "CIGARRETE", // oof + /obj/item/clothing/mask = "MASK", + /obj/item/clothing/glasses = "GLASSES", + /obj/item/clothing = "CLOTHING", + /obj/item/grenade/clusterbuster = "CLUSTERBUSTER", + /obj/item/grenade = "GRENADE", + /obj/item/gun = "GUN", + /obj/item/implant = "IMPLANT", + /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack = "MECHA_MISSILE_RACK", + /obj/item/mecha_parts/mecha_equipment/weapon = "MECHA_WEAPON", + /obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP", + /obj/item/melee = "MELEE", + /obj/item/mmi = "MMI", + /obj/item/nullrod = "NULLROD", + /obj/item/organ/external = "EXT_ORG", + /obj/item/organ/internal/cyberimp = "CYBERIMP", + /obj/item/organ/internal = "INT_ORG", + /obj/item/organ = "ORGAN", + /obj/item/pda = "PDA", + /obj/item/projectile = "PROJ", + /obj/item/radio/headset = "HEADSET", + /obj/item/reagent_containers/glass/beaker = "BEAKER", + /obj/item/reagent_containers/glass/bottle = "BOTTLE", + /obj/item/reagent_containers/food/pill/patch = "PATCH", + /obj/item/reagent_containers/food/pill = "PILL", + /obj/item/reagent_containers/food/drinks = "DRINK", + /obj/item/reagent_containers/food = "FOOD", + /obj/item/reagent_containers/syringe = "SYRINGE", + /obj/item/reagent_containers = "REAGENT_CONTAINERS", + /obj/item/robot_parts = "ROBOT_PARTS", + /obj/item/seeds = "SEED", + /obj/item/slime_extract = "SLIME_CORE", + /obj/item/spacepod_equipment/weaponry = "POD_WEAPON", + /obj/item/spacepod_equipment = "POD_EQUIP", + /obj/item/stack/sheet/mineral = "MINERAL", + /obj/item/stack/sheet = "SHEET", + /obj/item/stack/tile = "TILE", + /obj/item/stack = "STACK", + /obj/item/stock_parts/cell = "POWERCELL", + /obj/item/stock_parts = "STOCK_PARTS", + /obj/item/storage/firstaid = "FIRSTAID", + /obj/item/storage = "STORAGE", + /obj/item/tank = "GAS_TANK", + /obj/item/toy/crayon = "CRAYON", + /obj/item/toy = "TOY", + /obj/item = "ITEM", + /obj/machinery/atmospherics = "ATMOS_MACH", + /obj/machinery/computer = "CONSOLE", + /obj/machinery/door/airlock = "AIRLOCK", + /obj/machinery/door = "DOOR", + /obj/machinery/kitchen_machine = "KITCHEN", + /obj/machinery/portable_atmospherics/canister = "CANISTER", + /obj/machinery/portable_atmospherics = "PORT_ATMOS", + /obj/machinery/power = "POWER", + /obj/machinery/telecomms = "TCOMMS", + /obj/machinery = "MACHINERY", + /obj/mecha = "MECHA", + /obj/structure/closet/crate = "CRATE", + /obj/structure/closet = "CLOSET", + /obj/structure/statue = "STATUE", + /obj/structure/chair = "CHAIR", // oh no + /obj/structure/bed = "BED", + /obj/structure/chair/stool = "STOOL", + /obj/structure/table = "TABLE", + /obj/structure = "STRUCTURE", + /obj/vehicle = "VEHICLE", + /obj = "O", + /datum = "D", + /turf/simulated/floor = "SIM_FLOOR", + /turf/simulated/wall = "SIM_WALL", + /turf/unsimulated/floor = "UNSIM_FLOOR", + /turf/unsimulated/wall = "UNSIM_WALL", + /turf = "T", + /mob/living/carbon/alien = "XENO", + /mob/living/carbon/human = "HUMAN", + /mob/living/carbon = "CARBON", + /mob/living/silicon/robot = "CYBORG", + /mob/living/silicon/ai = "AI", + /mob/living/silicon = "SILICON", + /mob/living/simple_animal/bot = "BOT", + /mob/living/simple_animal = "SIMPLE", + /mob/living = "LIVING", + /mob = "M" + ) + for(var/tn in TYPES_SHORTCUTS) + if(copytext(typename, 1, length("[tn]/") + 1) == "[tn]/") + typename = TYPES_SHORTCUTS[tn]+copytext(typename,length("[tn]/")) + break + .[typename] = type + + +/proc/get_fancy_list_of_atom_types() + var/static/list/pre_generated_list + if(!pre_generated_list) //init + pre_generated_list = make_types_fancy(typesof(/atom)) + return pre_generated_list + + +/proc/get_fancy_list_of_datum_types() + var/static/list/pre_generated_list + if(!pre_generated_list) //init + pre_generated_list = make_types_fancy(sortList(typesof(/datum) - typesof(/atom))) + return pre_generated_list + + +/proc/filter_fancy_list(list/L, filter as text) + var/list/matches = new + for(var/key in L) + var/value = L[key] + if(findtext("[key]", filter) || findtext("[value]", filter)) + matches[key] = value + return matches + +//Key thing that stops lag. Cornerstone of performance in ss13, Just sitting here, in unsorted.dm. + +//Increases delay as the server gets more overloaded, +//as sleeps aren't cheap and sleeping only to wake up and sleep again is wasteful +#define DELTA_CALC max(((max(TICK_USAGE, world.cpu) / 100) * max(Master.sleep_delta-1,1)), 1) + +//returns the number of ticks slept +/proc/stoplag(initial_delay) + if(!Master || !(Master.current_runlevel & RUNLEVELS_DEFAULT)) + sleep(world.tick_lag) + return 1 + if(!initial_delay) + initial_delay = world.tick_lag + . = 0 + var/i = DS2TICKS(initial_delay) + do + . += CEILING(i*DELTA_CALC, 1) + sleep(i*world.tick_lag*DELTA_CALC) + i *= 2 + while(TICK_USAGE > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit)) + +#undef DELTA_CALC + +// This proc gets a list of all "points of interest" (poi's) that can be used by admins to track valuable mobs or atoms (such as the nuke disk). +/proc/getpois(mobs_only=0,skip_mindless=0) + var/list/mobs = sortmobs() + var/list/names = list() + var/list/pois = list() + var/list/namecounts = list() + + for(var/mob/M in mobs) + if(skip_mindless && (!M.mind && !M.ckey)) + if(!isbot(M) && !istype(M, /mob/camera/)) + continue + if(M.client && M.client.holder && M.client.holder.fakekey) //stealthmins + continue + var/name = M.name + if(name in names) + namecounts[name]++ + name = "[name] ([namecounts[name]])" + else + names.Add(name) + namecounts[name] = 1 + if(M.real_name && M.real_name != M.name) + name += " \[[M.real_name]\]" + if(M.stat == DEAD) + if(istype(M, /mob/dead/observer/)) + name += " \[ghost\]" + else + name += " \[dead\]" + pois[name] = M + + if(!mobs_only) + for(var/atom/A in GLOB.poi_list) + if(!A || !A.loc) + continue + var/name = A.name + if(names.Find(name)) + namecounts[name]++ + name = "[name] ([namecounts[name]])" + else + names.Add(name) + namecounts[name] = 1 + pois[name] = A + + return pois + +/proc/flash_color(mob_or_client, flash_color="#960000", flash_time=20) + var/client/C + if(istype(mob_or_client, /mob)) + var/mob/M = mob_or_client + if(M.client) + C = M.client + else + return + else if(istype(mob_or_client, /client)) + C = mob_or_client + + if(!istype(C)) + return + + C.color = flash_color + spawn(0) + animate(C, color = initial(C.color), time = flash_time) + +#define RANDOM_COLOUR (rgb(rand(0,255),rand(0,255),rand(0,255))) + +/proc/make_bit_triplet() + var/list/num_sample = list(1, 2, 3, 4, 5, 6, 7, 8, 9) + var/result = 0 + for(var/i = 0, i < 3, i++) + var/num = pick(num_sample) + num_sample -= num + result += (1 << num) + return result + +/proc/pixel_shift_dir(var/dir, var/amount_x = 32, var/amount_y = 32) //Returns a list with pixel_shift values that will shift an object's icon one tile in the direction passed. + amount_x = min(max(0, amount_x), 32) //No less than 0, no greater than 32. + amount_y = min(max(0, amount_x), 32) + var/list/shift = list("x" = 0, "y" = 0) + switch(dir) + if(NORTH) + shift["y"] = amount_y + if(SOUTH) + shift["y"] = -amount_y + if(EAST) + shift["x"] = amount_x + if(WEST) + shift["x"] = -amount_x + if(NORTHEAST) + shift = list("x" = amount_x, "y" = amount_y) + if(NORTHWEST) + shift = list("x" = -amount_x, "y" = amount_y) + if(SOUTHEAST) + shift = list("x" = amount_x, "y" = -amount_y) + if(SOUTHWEST) + shift = list("x" = -amount_x, "y" = -amount_y) + + return shift + +//Return a list of atoms in a location of a given type. Can be refined to look for pixel-shift. +/proc/get_atoms_of_type(var/atom/here, var/type, var/check_shift, var/shift_x = 0, var/shift_y = 0) + . = list() + if(here) + for(var/atom/thing in here) + if(istype(thing, type) && (check_shift && thing.pixel_x == shift_x && thing.pixel_y == shift_y)) + . += thing + +//gives us the stack trace from CRASH() without ending the current proc. +/proc/stack_trace(msg) + CRASH(msg) + +/datum/proc/stack_trace(msg) + CRASH(msg) + +/proc/pass() + return + +/atom/proc/Shake(pixelshiftx = 15, pixelshifty = 15, duration = 250) + var/initialpixelx = pixel_x + var/initialpixely = pixel_y + var/shiftx = rand(-pixelshiftx,pixelshiftx) + var/shifty = rand(-pixelshifty,pixelshifty) + animate(src, pixel_x = pixel_x + shiftx, pixel_y = pixel_y + shifty, time = 0.2, loop = duration) + pixel_x = initialpixelx + pixel_y = initialpixely + +/proc/params2turf(scr_loc, turf/origin, client/C) + if(!scr_loc) + return null + var/tX = splittext(scr_loc, ",") + var/tY = splittext(tX[2], ":") + var/tZ = origin.z + tY = tY[1] + tX = splittext(tX[1], ":") + tX = tX[1] + var/list/actual_view = getviewsize(C ? C.view : world.view) + tX = Clamp(origin.x + text2num(tX) - round(actual_view[1] / 2) - 1, 1, world.maxx) + tY = Clamp(origin.y + text2num(tY) - round(actual_view[2] / 2) - 1, 1, world.maxy) + return locate(tX, tY, tZ) diff --git a/code/controllers/subsystem/input.dm b/code/controllers/subsystem/input.dm new file mode 100644 index 000000000000..f2e0fdb04972 --- /dev/null +++ b/code/controllers/subsystem/input.dm @@ -0,0 +1,120 @@ +SUBSYSTEM_DEF(input) + name = "Input" + wait = 1 //SS_TICKER means this runs every tick + init_order = INIT_ORDER_INPUT + flags = SS_TICKER + priority = FIRE_PRIORITY_INPUT + runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY + + var/list/macro_sets + var/list/movement_keys + var/list/alt_movement_keys + +/datum/controller/subsystem/input/Initialize() + setup_default_macro_sets() + + setup_default_movement_keys() + + initialized = TRUE + + refresh_client_macro_sets() + + return ..() + +// This is for when macro sets are eventualy datumized +/datum/controller/subsystem/input/proc/setup_default_macro_sets() + var/list/static/default_macro_sets + + if(default_macro_sets) + macro_sets = default_macro_sets + return + + default_macro_sets = list( + "default" = list( + "Tab" = "\".winset \\\"input.focus=true?map.focus=true input.background-color=[COLOR_INPUT_DISABLED]:input.focus=true input.background-color=[COLOR_INPUT_ENABLED]\\\"\"", + "O" = "ooc", + "T" = "say", + "M" = "me", + "Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"", // This makes it so backspace can remove default inputs + "Any" = "\"KeyDown \[\[*\]\]\"", + "Any+UP" = "\"KeyUp \[\[*\]\]\"", + ), + "old_default" = list( + "Tab" = "\".winset \\\"mainwindow.macro=old_hotkeys map.focus=true input.background-color=[COLOR_INPUT_DISABLED]\\\"\"", + "Ctrl+T" = "say", + "Ctrl+O" = "ooc", + ), + "old_hotkeys" = list( + "Tab" = "\".winset \\\"mainwindow.macro=old_default input.focus=true input.background-color=[COLOR_INPUT_ENABLED]\\\"\"", + "O" = "ooc", + "T" = "say", + "M" = "me", + "Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"", // This makes it so backspace can remove default inputs + "Any" = "\"KeyDown \[\[*\]\]\"", + "Any+UP" = "\"KeyUp \[\[*\]\]\"", + ), + ) + + // Because i'm lazy and don't want to type all these out twice + var/list/old_default = default_macro_sets["old_default"] + + var/list/static/oldmode_keys = list( + "North", "East", "South", "West", + "Northeast", "Southeast", "Northwest", "Southwest", + "Insert", "Delete", "Ctrl", "Alt", + "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", + ) + + for(var/i in 1 to oldmode_keys.len) + var/key = oldmode_keys[i] + old_default[key] = "\"KeyDown [key]\"" + old_default["[key]+UP"] = "\"KeyUp [key]\"" + + var/list/static/oldmode_ctrl_override_keys = list( + "W" = "W", "A" = "A", "S" = "S", "D" = "D", // movement + "1" = "1", "2" = "2", "3" = "3", "4" = "4", // intent + "B" = "B", // resist + "E" = "E", // quick equip + "F" = "F", // intent left + "G" = "G", // intent right + "H" = "H", // stop pulling + "Q" = "Q", // drop + "R" = "R", // throw + "X" = "X", // switch hands + "Y" = "Y", // activate item + "Z" = "Z", // activate item + ) + + for(var/i in 1 to oldmode_ctrl_override_keys.len) + var/key = oldmode_ctrl_override_keys[i] + var/override = oldmode_ctrl_override_keys[key] + old_default["Ctrl+[key]"] = "\"KeyDown [override]\"" + old_default["Ctrl+[key]+UP"] = "\"KeyUp [override]\"" + + macro_sets = default_macro_sets + +// For initially setting up or resetting to default the movement keys +/datum/controller/subsystem/input/proc/setup_default_movement_keys() + var/static/list/default_movement_keys = list( + "W" = NORTH, "A" = WEST, "S" = SOUTH, "D" = EAST, // WASD + "North" = NORTH, "West" = WEST, "South" = SOUTH, "East" = EAST, // Arrow keys & Numpad + ) + var/static/list/azerty_movement_keys = list( + "Z" = NORTH, "Q" = WEST, "S" = SOUTH, "D" = EAST, // WASD + "North" = NORTH, "West" = WEST, "South" = SOUTH, "East" = EAST, // Arrow keys & Numpad + ) + movement_keys = default_movement_keys.Copy() + alt_movement_keys = azerty_movement_keys.Copy() + +// Badmins just wanna have fun ♪ +/datum/controller/subsystem/input/proc/refresh_client_macro_sets() + var/list/clients = GLOB.clients + for(var/i in 1 to clients.len) + var/client/user = clients[i] + user.set_macros() + +/datum/controller/subsystem/input/fire() + var/list/clients = GLOB.clients // Let's sing the list cache song + for(var/i in 1 to clients.len) + var/client/C = clients[i] + C.keyLoop() diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index 60718af65258..f694b6d2e757 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -21,7 +21,7 @@ /client/proc/debug_controller(controller in list("failsafe", "Master", "Ticker", "Air", "Jobs", "Sun", "Radio", "Configuration", "pAI", "Cameras", "Garbage", "Event", "Alarm", "Nano", "Vote", "Fires", - "Mob", "NPC Pool", "Shuttle", "Timer", "Weather", "Space", "Mob Hunt Server")) + "Mob", "NPC Pool", "Shuttle", "Timer", "Weather", "Space", "Mob Hunt Server","Input")) set category = "Debug" set name = "Debug Controller" set desc = "Debug the various periodic loop controllers for the game (be careful!)" @@ -97,5 +97,8 @@ if("Mob Hunt Server") debug_variables(SSmob_hunt) feedback_add_details("admin_verb","DMobHuntServer") + if("Input") + debug_variables(SSinput) + feedback_add_details("admin_verb","DInput") message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.") \ No newline at end of file diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 290f2eda770f..9b11e1c23d15 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -5,8 +5,8 @@ var/list/comp_lookup var/list/signal_procs var/signal_enabled = FALSE + var/datum_flags = NONE var/var_edited = FALSE //Warranty void if seal is broken - var/tmp/unique_datum_id = null #ifdef TESTING diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index d9845a2ce6de..adb3167d8e4c 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -3,8 +3,8 @@ /datum/proc/can_vv_get(var_name) return TRUE -/client/proc/can_vv_get(var_name) - return TRUE +// /client/proc/can_vv_get(var_name) +// return TRUE /datum/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited switch(var_name) @@ -18,7 +18,7 @@ . = TRUE -/client/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited +/client/vv_edit_var(var_name, var_value) //called whenever a var is edited switch(var_name) if("vars") return FALSE @@ -37,7 +37,7 @@ return debug_variable(var_name, list(), 0, src) return debug_variable(var_name, vars[var_name], 0, src) -/client/proc/vv_get_var(var_name) +/client/vv_get_var(var_name) switch(var_name) if("vars") return debug_variable(var_name, list(), 0, src) @@ -57,7 +57,7 @@ .["Delete"] = "?_src_=vars;delete=[UID()]" . += "---" -/client/proc/vv_get_dropdown() +/client/vv_get_dropdown() . = list() . += "---" .["Call Proc"] = "?_src_=vars;proc_call=[UID()]" diff --git a/code/datums/helper_datums/hotkey_modes.dm b/code/datums/helper_datums/hotkey_modes.dm deleted file mode 100644 index 39254b23485f..000000000000 --- a/code/datums/helper_datums/hotkey_modes.dm +++ /dev/null @@ -1,27 +0,0 @@ -/datum/hotkey_mode - var/name - var/macro_hotkeys_inactive - var/macro_hotkeys_active - var/mob/my_mob - -/datum/hotkey_mode/New(mob) - my_mob = mob - -/datum/hotkey_mode/proc/set_winset_values() - winset(my_mob, null, "mainwindow.macro=[macro_hotkeys_inactive] hotkey_toggle.is-checked=false input.focus=true input.background-color=#d3b5b5") - winset(my_mob, null, "hotkey_toggle.command=\".winset \\\"mainwindow.macro != [macro_hotkeys_inactive] ? mainwindow.macro=[macro_hotkeys_inactive] hotkey_toggle.is-checked=false input.focus=true input.background-color=#d3b5b5 : mainwindow.macro=[macro_hotkeys_active] hotkey_toggle.is-checked=true mapwindow.map.focus=true input.background-color=#f0f0f0\\\"\"") - -/datum/hotkey_mode/qwerty - name = "QWERTY" - macro_hotkeys_inactive = "macro" - macro_hotkeys_active = "hotkeymode" - -/datum/hotkey_mode/azerty - name = "AZERTY" - macro_hotkeys_inactive = "azertymacro" - macro_hotkeys_active = "azertyhotkeymode" - -/datum/hotkey_mode/cyborg - name = "Cyborg" - macro_hotkeys_inactive = "borgmacro" - macro_hotkeys_active = "borghotkeymode" \ No newline at end of file diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 11bd455880a5..486b553bb8ee 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -68,7 +68,7 @@ if(!(AM.can_be_pulled(src, state, force))) return FALSE - // If we're pulling something then drop what we're currently pulling and pull this instead. + // if we're pulling something then drop what we're currently pulling and pull this instead. if(pulling) if(state == 0) stop_pulling() @@ -118,7 +118,9 @@ if(pulling.anchored) stop_pulling() return - + if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //separated from our puller and not in the middle of a diagonal move. + pulledby.stop_pulling() + /atom/movable/proc/can_be_pulled(user, grab_state, force) if(src == user || !isturf(loc)) return FALSE @@ -141,36 +143,55 @@ . = ..() else //Diagonal move, split it into cardinal moves moving_diagonally = FIRST_DIAG_STEP - if(direct & 1) - if(direct & 4) - if(step(src, NORTH)) + var/first_step_dir + // The `&& moving_diagonally` checks are so that a forceMove taking + // place due to a Crossed, Bumped, etc. call will interrupt + // the second half of the diagonal movement, or the second attempt + // at a first half if step() fails because we hit something. + if(direct & NORTH) + if(direct & EAST) + if(step(src, NORTH) && moving_diagonally) + first_step_dir = NORTH moving_diagonally = SECOND_DIAG_STEP . = step(src, EAST) - else if(step(src, EAST)) + else if(moving_diagonally && step(src, EAST)) + first_step_dir = EAST moving_diagonally = SECOND_DIAG_STEP . = step(src, NORTH) - else if(direct & 8) - if(step(src, NORTH)) + else if(direct & WEST) + if(step(src, NORTH) && moving_diagonally) + first_step_dir = NORTH moving_diagonally = SECOND_DIAG_STEP . = step(src, WEST) - else if(step(src, WEST)) + else if(moving_diagonally && step(src, WEST)) + first_step_dir = WEST moving_diagonally = SECOND_DIAG_STEP . = step(src, NORTH) - else if(direct & 2) - if(direct & 4) - if(step(src, SOUTH)) + else if(direct & SOUTH) + if(direct & EAST) + if(step(src, SOUTH) && moving_diagonally) + first_step_dir = SOUTH moving_diagonally = SECOND_DIAG_STEP . = step(src, EAST) - else if(step(src, EAST)) + else if(moving_diagonally && step(src, EAST)) + first_step_dir = EAST moving_diagonally = SECOND_DIAG_STEP . = step(src, SOUTH) - else if(direct & 8) - if(step(src, SOUTH)) + else if(direct & WEST) + if(step(src, SOUTH) && moving_diagonally) + first_step_dir = SOUTH moving_diagonally = SECOND_DIAG_STEP . = step(src, WEST) - else if(step(src, WEST)) + else if(moving_diagonally && step(src, WEST)) + first_step_dir = WEST moving_diagonally = SECOND_DIAG_STEP . = step(src, SOUTH) + if(moving_diagonally == SECOND_DIAG_STEP) + if(!.) + setDir(first_step_dir) + else if(!inertia_moving) + inertia_next_move = world.time + inertia_move_delay + newtonian_move(direct) moving_diagonally = 0 return @@ -191,7 +212,7 @@ // Called after a successful Move(). By this point, we've already moved /atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE) SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, OldLoc, Dir, Forced) - if (!inertia_moving) + if(!inertia_moving) inertia_next_move = world.time + inertia_move_delay newtonian_move(Dir) return TRUE @@ -214,6 +235,7 @@ /atom/movable/proc/forceMove(atom/destination) var/turf/old_loc = loc loc = destination + moving_diagonally = 0 if(old_loc) old_loc.Exited(src, destination) @@ -243,7 +265,7 @@ return 1 /atom/movable/proc/onTransitZ(old_z,new_z) - for(var/item in src) // Notify contents of Z-transition. This can be overridden IF we know the items contents do not care. + for(var/item in src) // Notify contents of Z-transition. This can be overridden if we know the items contents do not care. var/atom/movable/AM = item AM.onTransitZ(old_z,new_z) diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm index 0434638cc9ab..f2d39dcd8ca1 100644 --- a/code/game/objects/structures/mineral_doors.dm +++ b/code/game/objects/structures/mineral_doors.dm @@ -35,7 +35,7 @@ /obj/structure/mineral_door/Move() var/turf/T = loc - ..() + . = ..() move_update_air(T) /obj/structure/mineral_door/Bumped(atom/user) diff --git a/code/game/objects/structures/target_stake.dm b/code/game/objects/structures/target_stake.dm index a51f58e5ec64..591157e8bd6a 100644 --- a/code/game/objects/structures/target_stake.dm +++ b/code/game/objects/structures/target_stake.dm @@ -13,7 +13,7 @@ return ..() /obj/structure/target_stake/Move() - ..() + . = ..() // Move the pinned target along with the stake if(pinned_target in view(3, src)) pinned_target.loc = loc diff --git a/code/game/objects/structures/windoor_assembly.dm b/code/game/objects/structures/windoor_assembly.dm index acacb4e91530..1124745d8b3c 100644 --- a/code/game/objects/structures/windoor_assembly.dm +++ b/code/game/objects/structures/windoor_assembly.dm @@ -46,7 +46,7 @@ obj/structure/windoor_assembly/Destroy() /obj/structure/windoor_assembly/Move() var/turf/T = loc - ..() + . = ..() setDir(ini_dir) move_update_air(T) diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index e63451de8b63..16851097844b 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -431,7 +431,7 @@ var/global/wcCommon = pick(list("#379963", "#0d8395", "#58b5c3", "#49e46e", "#8f /obj/structure/window/Move() var/turf/T = loc - ..() + . = ..() setDir(ini_dir) move_update_air(T) diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index a1f56c52c781..685dc1eff2e4 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -185,3 +185,7 @@ var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey"," else send2irc(source, "[msg] - All admins AFK ([admin_number_afk]/[admin_number_total]) or skipped ([admin_number_ignored]/[admin_number_total])") return admin_number_present + +/client/proc/get_adminhelp() + var/msg = input(src, "Please describe your problem concisely and an admin will help as soon as they're able.", "Adminhelp contents") as text|null + adminhelp(msg) \ No newline at end of file diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index e3fe5af32638..6f6b34849240 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -17,6 +17,10 @@ feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/client/proc/get_admin_say() + var/msg = input(src, null, "asay \"text\"") as text|null + cmd_admin_say(msg) + /client/proc/cmd_mentor_say(msg as text) set category = "Admin" set name = "Msay" diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index 3874e26e9de9..e1bbc885c751 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -43,3 +43,7 @@ say_dead_direct("[prefix] says, \"[msg]\"") feedback_add_details("admin_verb","D") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/get_dead_say() + var/msg = input(src, null, "dsay \"text\"") as text + dsay(msg) \ No newline at end of file diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 0d5a866e6c56..f2fde8df758f 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -116,7 +116,7 @@ a_right.holder_movement() /obj/item/assembly_holder/Move() - ..() + . = ..() process_movement() return diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index d6da1fb4d0ec..98e59a7cbb50 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -106,7 +106,7 @@ /obj/item/assembly/infra/Move() var/t = dir - ..() + . = ..() dir = t qdel(first) diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index 995ee5194745..fd89434331ea 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -1,4 +1,8 @@ /client + ////////////////////// + //BLACK MAGIC THINGS// + ////////////////////// + parent_type = /datum //////////////// //ADMIN THINGS// //////////////// @@ -24,7 +28,7 @@ var/adminhelped = 0 - var/gc_destroyed //Time when this object was destroyed. + // var/gc_destroyed //Time when this object was destroyed. [Inherits from datum] #ifdef TESTING var/running_find_references @@ -89,5 +93,5 @@ // If set to true, this client can interact with atoms such as buttons and doors on top of regular machinery interaction var/advanced_admin_interaction = FALSE - // Has the client been varedited by an admin? - var/var_edited = FALSE + // Has the client been varedited by an admin? [Inherits from datum now] + // var/var_edited = FALSE diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 922502698faf..4e59dc2c1cfc 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -297,14 +297,14 @@ return null if(byond_build < config.minimum_client_build) alert(src, "You are using a byond build which is not supported by this server. Please use a build version of atleast [config.minimum_client_build].", "Incorrect build", "OK") - del(src) + qdel(src) return if(byond_version < SUGGESTED_CLIENT_VERSION) // Update is suggested, but not required. to_chat(src,"Your BYOND client (v: [byond_version]) is out of date. This can cause glitches. We highly suggest you download the latest client from http://www.byond.com/ before playing. ") if(IsGuestKey(key)) alert(src,"This server doesn't allow guest accounts to play. Please go to http://www.byond.com/ and register for a key.","Guest","OK") - del(src) + qdel(src) return // Change the way they should download resources. @@ -317,7 +317,6 @@ GLOB.clients += src GLOB.directory[ckey] = src - //Admin Authorisation // Automatically makes localhost connection an admin if(!config.disable_localhost_admin) @@ -365,11 +364,14 @@ . = ..() //calls mob.Login() + if(ckey in clientmessages) for(var/message in clientmessages[ckey]) to_chat(src, message) clientmessages.Remove(ckey) + if(SSinput.initialized) + set_macros() donator_check() check_ip_intel() @@ -404,8 +406,7 @@ if(!winexists(src, "asset_cache_browser")) // The client is using a custom skin, tell them. to_chat(src, "Unable to access asset cache browser, if you are using a custom skin file, please allow DS to download the updated version, if you are not, then make a bug report. This is not a critical issue but can cause issues with resource downloading, as it is impossible to know when extra resources arrived to you.") - - + //This is down here because of the browse() calls in tooltip/New() if(!tooltips) tooltips = new /datum/tooltip(src) @@ -664,7 +665,7 @@ sleep(10) // Since browse is non-instant, and kinda async to_chat(src, "
you're a huge nerd. wakka wakka doodle doop nobody's ever gonna see this, the chat system shouldn't be online by this point
") - del(src) + qdel(src) return TRUE else if (!topic || !topic["token"] || !tokens[ckey] || topic["token"] != tokens[ckey]) @@ -675,7 +676,7 @@ tokens[ckey] = cid_check_reconnect() sleep(10) //browse is queued, we don't want them to disconnect before getting the browse() command. - del(src) + qdel(src) return TRUE // We DO have their cached CID handy - compare it, now if(oldcid != computer_id) @@ -693,7 +694,7 @@ log_adminwarn("Failed Login: [key] [computer_id] [address] - CID randomizer confirmed (oldcid: [oldcid])") - del(src) + qdel(src) return TRUE else // don't shoot, I'm innocent @@ -795,18 +796,15 @@ /* Mainwindow */ winset(src, "mainwindow.saybutton", "background-color=#40628a;text-color=#FFFFFF") winset(src, "mainwindow.mebutton", "background-color=#40628a;text-color=#FFFFFF") - winset(src, "mainwindow.hotkey_toggle", "background-color=#40628a;text-color=#FFFFFF") ///// UI ELEMENTS ///// /* Mainwindow */ winset(src, "mainwindow", "background-color=#272727") winset(src, "mainwindow.mainvsplit", "background-color=#272727") winset(src, "mainwindow.tooltip", "background-color=#272727") /* Outputwindow */ - winset(src, "outputwindow.outputwindow", "background-color=#272727") winset(src, "outputwindow.browseroutput", "background-color=#272727") /* Rpane */ winset(src, "rpane", "background-color=#272727") - winset(src, "rpane.rpane", "background-color=#272727") winset(src, "rpane.rpanewindow", "background-color=#272727") /* Browserwindow */ winset(src, "browserwindow", "background-color=#272727") @@ -830,18 +828,15 @@ /* Mainwindow */ winset(src, "mainwindow.saybutton", "background-color=none;text-color=#000000") winset(src, "mainwindow.mebutton", "background-color=none;text-color=#000000") - winset(src, "mainwindow.hotkey_toggle", "background-color=none;text-color=#000000") ///// UI ELEMENTS ///// /* Mainwindow */ winset(src, "mainwindow", "background-color=none") winset(src, "mainwindow.mainvsplit", "background-color=none") winset(src, "mainwindow.tooltip", "background-color=none") /* Outputwindow */ - winset(src, "outputwindow.outputwindow", "background-color=none") winset(src, "outputwindow.browseroutput", "background-color=none") /* Rpane */ winset(src, "rpane", "background-color=none") - winset(src, "rpane.rpane", "background-color=none") winset(src, "rpane.rpanewindow", "background-color=none") /* Browserwindow */ winset(src, "browserwindow", "background-color=none") diff --git a/code/modules/client/preference/preferences_toggles.dm b/code/modules/client/preference/preferences_toggles.dm index 8096a7079162..e164a2ab8431 100644 --- a/code/modules/client/preference/preferences_toggles.dm +++ b/code/modules/client/preference/preferences_toggles.dm @@ -241,7 +241,31 @@ to_chat(usr, "You have enabled text popup limiting.") else to_chat(usr, "You have disabled text popup limiting.") + return +/client/verb/numpad_target() + set name = "Toggle Numpad targetting" + set category = "Preferences" + set desc = "This button will allow you to enable or disable Numpad Targetting" + prefs.toggles ^= NUMPAD_TARGET + prefs.save_preferences(src) + if (prefs.toggles & NUMPAD_TARGET) + to_chat(usr, "You have enabled Numpad Targetting.") + else + to_chat(usr, "You have disabled Numpad Targetting.") + return + +/client/verb/azerty_toggle() + set name = "Toggle QWERTY/AZERTY" + set category = "Preferences" + set desc = "This button will switch you between QWERTY and AZERTY control sets" + prefs.toggles ^= AZERTY + prefs.save_preferences(src) + if (prefs.toggles & AZERTY) + to_chat(usr, "You are now in AZERTY mode.") + else + to_chat(usr, "You are now in QWERTY mode.") + return /client/verb/toggle_ghost_pda() set name = "Show/Hide GhostPDA" set category = "Preferences" @@ -249,4 +273,4 @@ prefs.toggles ^= CHAT_GHOSTPDA to_chat(src, "As a ghost, you will now [(prefs.toggles & CHAT_GHOSTPDA) ? "see all PDA messages" : "no longer see PDA messages"].") prefs.save_preferences(src) - feedback_add_details("admin_verb","TGP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! \ No newline at end of file + feedback_add_details("admin_verb","TGP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/keybindings/bindings_admin.dm b/code/modules/keybindings/bindings_admin.dm new file mode 100644 index 000000000000..ca232adbe0e7 --- /dev/null +++ b/code/modules/keybindings/bindings_admin.dm @@ -0,0 +1,24 @@ +/datum/admins/key_down(_key, client/user) + switch(_key) + if("F3") + user.get_admin_say() + return + if("F5") + user.admin_ghost() + return + if("F6") + player_panel_new() + return + if("F7") + user.togglebuildmodeself() + return + if("F8") + if(user.keys_held["Ctrl"]) + user.stealth() + else + user.invisimin() + return + if("F10") + user.get_dead_say() + return + ..() diff --git a/code/modules/keybindings/bindings_atom.dm b/code/modules/keybindings/bindings_atom.dm new file mode 100644 index 000000000000..6b1e0afe199c --- /dev/null +++ b/code/modules/keybindings/bindings_atom.dm @@ -0,0 +1,21 @@ +// You might be wondering why this isn't client level. If focus is null, we don't want you to move. +// Only way to do that is to tie the behavior into the focus's keyLoop(). + +/atom/movable/keyLoop(client/user) + if(!user.keys_held["Ctrl"]) + var/movement_dir = NONE + var/list/movement = SSinput.movement_keys + if(user.prefs.toggles & AZERTY) + movement = SSinput.alt_movement_keys + for(var/_key in user.keys_held) + movement_dir = movement_dir | movement[_key] + if(user.next_move_dir_add) + movement_dir |= user.next_move_dir_add + if(user.next_move_dir_sub) + movement_dir &= ~user.next_move_dir_sub + // Sanity checks in case you hold left and right and up to make sure you only go up + if((movement_dir & NORTH) && (movement_dir & SOUTH)) + movement_dir &= ~(NORTH|SOUTH) + if((movement_dir & EAST) && (movement_dir & WEST)) + movement_dir &= ~(EAST|WEST) + user.Move(get_step(src, movement_dir), movement_dir) \ No newline at end of file diff --git a/code/modules/keybindings/bindings_carbon.dm b/code/modules/keybindings/bindings_carbon.dm new file mode 100644 index 000000000000..d17f10e963f3 --- /dev/null +++ b/code/modules/keybindings/bindings_carbon.dm @@ -0,0 +1,18 @@ +/mob/living/carbon/key_down(_key, client/user) + switch(_key) + if("R", "Southwest") // Southwest is End + toggle_throw_mode() + return + if("1") + a_intent_change("help") + return + if("2") + a_intent_change("disarm") + return + if("3") + a_intent_change("grab") + return + if("4") + a_intent_change("harm") + return + return ..() \ No newline at end of file diff --git a/code/modules/keybindings/bindings_client.dm b/code/modules/keybindings/bindings_client.dm new file mode 100644 index 000000000000..cea95f0e5b13 --- /dev/null +++ b/code/modules/keybindings/bindings_client.dm @@ -0,0 +1,53 @@ +// Clients aren't datums so we have to define these procs indpendently. +// These verbs are called for all key press and release events +/client/verb/keyDown(_key as text) + set instant = TRUE + set hidden = TRUE + keys_held[_key] = world.time + var/movement = SSinput.movement_keys[_key] + if (prefs.toggles & AZERTY) movement = SSinput.alt_movement_keys[_key] + if(!(next_move_dir_sub & movement) && !keys_held["Ctrl"]) + next_move_dir_add |= movement + + // Client-level keybindings are ones anyone should be able to do at any time + // Things like taking screenshots, hitting tab, and adminhelps. + + switch(_key) + if("F1") + if(keys_held["Ctrl"] && keys_held["Shift"]) // Is this command ever used? + winset(src, null, "command=.options") + else + get_adminhelp() + return + if("F2") // Screenshot. Hold shift to choose a name and location to save in + winset(src, null, "command=.screenshot [!keys_held["shift"] ? "auto" : ""]") + return + if("F12") // Toggles minimal HUD + mob.button_pressed_F12() + return + + if(holder) + holder.key_down(_key, src) + if(mob.focus) + mob.focus.key_down(_key, src) + +/client/verb/keyUp(_key as text) + set instant = TRUE + set hidden = TRUE + keys_held -= _key + var/movement = SSinput.movement_keys[_key] + if (prefs.toggles & AZERTY) movement = SSinput.alt_movement_keys[_key] + if(!(next_move_dir_add & movement)) + next_move_dir_sub |= movement + + if(holder) + holder.key_up(_key, src) + if(mob.focus) + mob.focus.key_up(_key, src) + +// Called every game tick +/client/keyLoop() + if(holder) + holder.keyLoop(src) + if(mob.focus) + mob.focus.keyLoop(src) \ No newline at end of file diff --git a/code/modules/keybindings/bindings_human.dm b/code/modules/keybindings/bindings_human.dm new file mode 100644 index 000000000000..5e8ced96ab6a --- /dev/null +++ b/code/modules/keybindings/bindings_human.dm @@ -0,0 +1,80 @@ +/mob/living/carbon/human/key_down(_key, client/user) + if(_key == "H") + var/obj/item/clothing/accessory/holster/H = null + if(istype(w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/S = w_uniform + if(S.accessories.len) + H = locate() in S.accessories + if (!H) + return + if(!H.holstered) + if(!istype(get_active_hand(), /obj/item/gun)) + to_chat(usr, "You need your gun equiped to holster it.") + return + var/obj/item/gun/W = get_active_hand() + H.holster(W, usr) + else + H.unholster(usr) + if(client.keys_held["Shift"]) + switch(_key) + if("E") // Put held thing in belt or take out most recent thing from belt + quick_equip() // Implementing the storage component is going to take way too long + // var/obj/item/thing = get_active_hand() + // var/obj/item/equipped_belt = get_item_by_slot(slot_belt) + // if(!equipped_belt) // We also let you equip a belt like this + // if(!thing) + // to_chat(user, "You have no belt to take something out of.") + // return + // if(equip_to_slot_if_possible(thing, slot_belt)) + // update_inv_r_hand() + // update_inv_l_hand() + // return + // if(!SEND_SIGNAL(equipped_belt, COMSIG_CONTAINS_STORAGE)) // not a storage item + // if(!thing) + // equipped_belt.attack_hand(src) + // else + // to_chat(user, "You can't fit anything in.") + // return + // if(thing) // put thing in belt + // if(!SEND_SIGNAL(equipped_belt, COMSIG_TRY_STORAGE_INSERT, thing, user.mob)) + // to_chat(user, "You can't fit anything in.") + // return + // if(!equipped_belt.contents.len) // nothing to take out + // to_chat(user, "There's nothing in your belt to take out.") + // return + // var/obj/item/stored = equipped_belt.contents[equipped_belt.contents.len] + // if(!stored || stored.on_found(src)) + // return + // stored.attack_hand(src) // take out thing from belt + // return + + /* if("B") // Put held thing in backpack or take out most recent thing from backpack + var/obj/item/thing = get_active_hand() + var/obj/item/equipped_back = get_item_by_slot(slot_back) + if(!equipped_back) // We also let you equip a backpack like this + if(!thing) + to_chat(user, "You have no backpack to take something out of.") + return + if(equip_to_slot_if_possible(thing, slot_back)) + update_inv_r_hand() + update_inv_l_hand() + return + if(!SEND_SIGNAL(equipped_back, COMSIG_CONTAINS_STORAGE)) // not a storage item + if(!thing) + equipped_back.attack_hand(src) + else + to_chat(user, "You can't fit anything in.") + return + if(thing) // put thing in backpack + if(!SEND_SIGNAL(equipped_back, COMSIG_TRY_STORAGE_INSERT, thing, user.mob)) + to_chat(user, "You can't fit anything in.") + return + if(!equipped_back.contents.len) // nothing to take out + to_chat(user, "There's nothing in your backpack to take out.") + return + var/obj/item/stored = equipped_back.contents[equipped_back.contents.len] + if(!stored || stored.on_found(src)) + return + stored.attack_hand(src) // take out thing from backpack + return */ + return ..() \ No newline at end of file diff --git a/code/modules/keybindings/bindings_living.dm b/code/modules/keybindings/bindings_living.dm new file mode 100644 index 000000000000..241bc15b608a --- /dev/null +++ b/code/modules/keybindings/bindings_living.dm @@ -0,0 +1,7 @@ +/mob/living/key_down(_key, client/user) + switch(_key) + if("B") + resist() + return + + return ..() \ No newline at end of file diff --git a/code/modules/keybindings/bindings_mob.dm b/code/modules/keybindings/bindings_mob.dm new file mode 100644 index 000000000000..95e542ab938c --- /dev/null +++ b/code/modules/keybindings/bindings_mob.dm @@ -0,0 +1,132 @@ +// Technically the client argument is unncessary here since that SHOULD be src.client but let's not assume things +// All it takes is one badmin setting their focus to someone else's client to mess things up +// Or we can have NPC's send actual keypresses and detect that by seeing no client + +/mob/key_down(_key, client/user) + if(user.prefs.toggles & AZERTY) + switch(_key) + if("Delete") + if(!pulling) + to_chat(src, "You are not pulling anything.") + else + stop_pulling() + return + if("Insert", "G") + a_intent_change(INTENT_HOTKEY_RIGHT) + return + if("F") + a_intent_change(INTENT_HOTKEY_LEFT) + return + if("X", "Northeast") // Northeast is Page-up + swap_hand() + return + if("Y", "W", "Southeast") // Southeast is Page-down + mode() // attack_self(). No idea who came up with "mode()" + return + if("A", "Northwest") // Northwest is Home + var/obj/item/I = get_active_hand() + if(!I) + to_chat(src, "You have nothing to drop in your hand!") + else + drop_item(I) + return + if("E") + quick_equip() + return + if("Alt") + toggle_move_intent() + return + else + switch(_key) + if("Delete") + if(!pulling) + to_chat(src, "You are not pulling anything.") + else + stop_pulling() + return + if("Insert", "G") + a_intent_change(INTENT_HOTKEY_RIGHT) + return + if("F") + a_intent_change(INTENT_HOTKEY_LEFT) + return + if("X", "Northeast") // Northeast is Page-up + swap_hand() + return + if("Y", "Z", "Southeast") // Southeast is Page-down + mode() // attack_self(). No idea who came up with "mode()" + return + if("Q", "Northwest") // Northwest is Home + var/obj/item/I = get_active_hand() + if(!I) + to_chat(src, "You have nothing to drop in your hand!") + else + drop_item(I) + return + if("E") + quick_equip() + return + if("Alt") + toggle_move_intent() + return + //Bodypart selections + if(client.prefs.toggles & NUMPAD_TARGET) + switch(_key) + if("Numpad8") + user.body_toggle_head() + return + if("Numpad4") + user.body_r_arm() + return + if("Numpad5") + user.body_chest() + return + if("Numpad6") + user.body_l_arm() + return + if("Numpad1") + user.body_r_leg() + return + if("Numpad2") + user.body_groin() + return + if("Numpad3") + user.body_l_leg() + return + + if(client.keys_held["Ctrl"] && client.prefs.toggles & AZERTY) + switch(SSinput.alt_movement_keys[_key]) + if(NORTH) + northface() + return + if(SOUTH) + southface() + return + if(WEST) + westface() + return + if(EAST) + eastface() + return + else if(client.keys_held["Ctrl"]) + switch(SSinput.movement_keys[_key]) + if(NORTH) + northface() + return + if(SOUTH) + southface() + return + if(WEST) + westface() + return + if(EAST) + eastface() + return + return ..() + +/mob/key_up(_key, client/user) + switch(_key) + if("Alt") + toggle_move_intent() + return + return ..() \ No newline at end of file diff --git a/code/modules/keybindings/bindings_robot.dm b/code/modules/keybindings/bindings_robot.dm new file mode 100644 index 000000000000..2354f33c9e9d --- /dev/null +++ b/code/modules/keybindings/bindings_robot.dm @@ -0,0 +1,12 @@ +/mob/living/silicon/robot/key_down(_key, client/user) + switch(_key) + if("1", "2", "3") + toggle_module(text2num(_key)) + return + if("4") + a_intent_change(INTENT_HOTKEY_LEFT) + return + if("Q") + uneq_active() + return + return ..() \ No newline at end of file diff --git a/code/modules/keybindings/focus.dm b/code/modules/keybindings/focus.dm new file mode 100644 index 000000000000..9cfbf36c5d38 --- /dev/null +++ b/code/modules/keybindings/focus.dm @@ -0,0 +1,8 @@ +/mob + var/datum/focus //What receives our keyboard inputs. src by default + +/mob/proc/set_focus(datum/new_focus) + if(focus == new_focus) + return + focus = new_focus + reset_perspective(focus) //Maybe this should be done manually? You figure it out, reader \ No newline at end of file diff --git a/code/modules/keybindings/readme.md b/code/modules/keybindings/readme.md new file mode 100644 index 000000000000..f57d8d55ffab --- /dev/null +++ b/code/modules/keybindings/readme.md @@ -0,0 +1,42 @@ +# In-code keypress handling system + +This whole system is heavily based off of forum_account's keyboard library. +Thanks to forum_account for saving the day, the library can be found +[here](https://secure.byond.com/developer/Forum_account/Keyboard)! + +.dmf macros have some very serious shortcomings. For example, they do not allow reusing parts +of one macro in another, so giving cyborgs their own shortcuts to swap active module couldn't +inherit the movement that all mobs should have anyways. The webclient only supports one macro, +so having more than one was problematic. Additionally each keybind has to call an actual +verb, which meant a lot of hidden verbs that just call one other proc. Also our existing +macro was really bad and tied unrelated behavior into `Northeast()`, `Southeast()`, `Northwest()`, +and `Southwest()`. + +The basic premise of this system is to not screw with .dmf macro setup at all and handle +pressing those keys in the code instead. We have every key call `client.keyDown()` +or `client.keyUp()` with the pressed key as an argument. Certain keys get processed +directly by the client because they should be doable at any time, then we call +`keyDown()` or `keyUp()` on the client's holder and the client's mob's focus. +By default `mob.focus` is the mob itself, but you can set it to any datum to give control of a +client's keypresses to another object. This would be a good way to handle a menu or driving +a mech. You can also set it to null to disregard input from a certain user. + +Movement is handled by having each client call `client.keyLoop()` every game tick. +As above, this calls holder and `focus.keyLoop()`. `atom/movable/keyLoop()` handles movement +Try to keep the calculations in this proc light. It runs every tick for every client after all! + +You can also tell which keys are being held down now. Each client a list of keys pressed called +`keys_held`. Each entry is a key as a text string associated with the world.time when it was +pressed. + +No client-set keybindings at this time, but it shouldn't be too hard if someone wants. + +Notes about certain keys: + +* `Tab` has client-sided behavior but acts normally +* `T`, `O`, and `M` move focus to the input when pressed. This fires the keyUp macro right away. +* `\` needs to be escaped in the dmf so any usage is `\\` + +You cannot `TICK_CHECK` or check `world.tick_usage` inside of procs called by key down and up +events. They happen outside of a byond tick and have no meaning there. Key looping +works correctly since it's part of a subsystem, not direct input. \ No newline at end of file diff --git a/code/modules/keybindings/setup.dm b/code/modules/keybindings/setup.dm new file mode 100644 index 000000000000..ad87622b709b --- /dev/null +++ b/code/modules/keybindings/setup.dm @@ -0,0 +1,46 @@ +/client + var/list/keys_held = list() // A list of any keys held currently + // These next two vars are to apply movement for keypresses and releases made while move delayed. + // Because discarding that input makes the game less responsive. + var/next_move_dir_add // On next move, add this dir to the move that would otherwise be done + var/next_move_dir_sub // On next move, subtract this dir from the move that would otherwise be done + +// Set a client's focus to an object and override these procs on that object to let it handle keypresses + +/datum/proc/key_down(key, client/user) // Called when a key is pressed down initially + return +/datum/proc/key_up(key, client/user) // Called when a key is released + return +/datum/proc/keyLoop(client/user) // Called once every frame + set waitfor = FALSE + return + +// removes all the existing macros +/client/proc/erase_all_macros() + var/list/macro_sets = params2list(winget(src, null, "macros")) + var/erase_output = "" + for(var/i in 1 to macro_sets.len) + var/setname = macro_sets[i] + var/list/macro_set = params2list(winget(src, "[setname].*", "command")) // The third arg doesnt matter here as we're just removing them all + for(var/k in 1 to macro_set.len) + var/list/split_name = splittext(macro_set[k], ".") + var/macro_name = "[split_name[1]].[split_name[2]]" // [3] is "command" + erase_output = "[erase_output];[macro_name].parent=null" + winset(src, null, erase_output) + +/client/proc/set_macros() + set waitfor = FALSE + + erase_all_macros() + + var/list/macro_sets = SSinput.macro_sets + for(var/i in 1 to macro_sets.len) + var/setname = macro_sets[i] + if(setname != "default") + winclone(src, "default", setname) + var/list/macro_set = macro_sets[setname] + for(var/k in 1 to macro_set.len) + var/key = macro_set[k] + var/command = macro_set[key] + winset(src, "[setname]-\ref[key]", "parent=[setname];name=[key];command=[command]") + winset(src, null, "input.focus=true input.background-color=[COLOR_INPUT_ENABLED] mainwindow.macro=default") diff --git a/code/modules/mining/explorer_gear.dm b/code/modules/mining/explorer_gear.dm index 0cad683438f5..a9464efe959f 100644 --- a/code/modules/mining/explorer_gear.dm +++ b/code/modules/mining/explorer_gear.dm @@ -1,44 +1,44 @@ -/****************Explorer's Suit and Mask****************/ -/obj/item/clothing/suit/hooded/explorer - name = "explorer suit" - desc = "An armoured suit for exploring harsh environments." - icon_state = "explorer" - item_state = "explorer" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - hoodtype = /obj/item/clothing/head/hooded/explorer - armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50) - allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe) - resistance_flags = FIRE_PROOF - hide_tail_by_species = list("Vox" , "Vulpkanin" , "Unathi" , "Tajaran") - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi', - "Drask" = 'icons/mob/species/drask/suit.dmi', - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi' - ) - -/obj/item/clothing/head/hooded/explorer - name = "explorer hood" - desc = "An armoured hood for exploring harsh environments." - icon_state = "explorer" - item_state = "explorer" - body_parts_covered = HEAD - flags = BLOCKHAIR | NODROP - flags_cover = HEADCOVERSEYES - min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT - max_heat_protection_temperature = FIRE_HELM_MAX_TEMP_PROTECT - armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50) - resistance_flags = FIRE_PROOF - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/head.dmi', - "Drask" = 'icons/mob/species/drask/head.dmi', - "Grey" = 'icons/mob/species/grey/head.dmi', - "Skrell" = 'icons/mob/species/skrell/head.dmi' +/****************Explorer's Suit and Mask****************/ +/obj/item/clothing/suit/hooded/explorer + name = "explorer suit" + desc = "An armoured suit for exploring harsh environments." + icon_state = "explorer" + item_state = "explorer" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + hoodtype = /obj/item/clothing/head/hooded/explorer + armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50) + allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe) + resistance_flags = FIRE_PROOF + hide_tail_by_species = list("Vox" , "Vulpkanin" , "Unathi" , "Tajaran") + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi', + "Drask" = 'icons/mob/species/drask/suit.dmi', + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi' + ) + +/obj/item/clothing/head/hooded/explorer + name = "explorer hood" + desc = "An armoured hood for exploring harsh environments." + icon_state = "explorer" + item_state = "explorer" + body_parts_covered = HEAD + flags = BLOCKHAIR | NODROP + flags_cover = HEADCOVERSEYES + min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT + max_heat_protection_temperature = FIRE_HELM_MAX_TEMP_PROTECT + armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 50) + resistance_flags = FIRE_PROOF + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/head.dmi', + "Drask" = 'icons/mob/species/drask/head.dmi', + "Grey" = 'icons/mob/species/grey/head.dmi', + "Skrell" = 'icons/mob/species/skrell/head.dmi' ) \ No newline at end of file diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index bb7bcfc94e13..35a08d0ce10d 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -6,6 +6,14 @@ if(hand) return l_hand else return r_hand +/mob/verb/quick_equip() + set name = "quick-equip" + set hidden = 1 + + var/obj/item/I = get_active_hand() + if(I) + I.equip_to_best_slot(src) + /mob/proc/is_in_active_hand(obj/item/I) var/obj/item/item_to_test = get_active_hand() @@ -254,3 +262,4 @@ if(slot_r_hand) return r_hand return null + diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 7eecc1ff00ad..a8dbaa7ac2fd 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -975,6 +975,7 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump, to_chat(src, "You [slipVerb]ped on [description]!") playsound(src.loc, 'sound/misc/slip.ogg', 50, 1, -3) // Something something don't run with scissors + moving_diagonally = 0 //If this was part of diagonal move slipping will stop it. Stun(stun) Weaken(weaken) return 1 diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index 47dd5622379e..cf9fcfd632c9 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -1,11 +1,3 @@ -/mob/living/carbon/human/verb/quick_equip() - set name = "quick-equip" - set hidden = 1 - - var/obj/item/I = get_active_hand() - if(I) - I.equip_to_best_slot(src) - /mob/living/carbon/human/proc/equip_in_one_of_slots(obj/item/W, list/slots, del_on_fail = 1) for(var/slot in slots) if(equip_to_slot_if_possible(W, slots[slot], del_on_fail = 0)) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 727c900ac9ca..f9dba949b4b6 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -528,7 +528,7 @@ return var/pull_dir = get_dir(src, pulling) - if(get_dist(src, pulling) > 1 || ((pull_dir - 1) & pull_dir)) // puller and pullee more than one tile away or in diagonal position + if(get_dist(src, pulling) > 1 || (moving_diagonally != SECOND_DIAG_STEP && ((pull_dir - 1) & pull_dir))) // puller and pullee more than one tile away or in diagonal position if(isliving(pulling)) var/mob/living/M = pulling if(M.lying && !M.buckled && (prob(M.getBruteLoss() * 200 / M.maxHealth))) diff --git a/code/modules/mob/living/silicon/robot/login.dm b/code/modules/mob/living/silicon/robot/login.dm index d0262df743cf..cf55cdd5f911 100644 --- a/code/modules/mob/living/silicon/robot/login.dm +++ b/code/modules/mob/living/silicon/robot/login.dm @@ -2,6 +2,3 @@ ..() regenerate_icons() show_laws(0) - - var/datum/hotkey_mode/cyborg/C = new(src) - C.set_winset_values() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm index 5fd5b4e92310..7fd32bd71ec8 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm @@ -1,265 +1,265 @@ -#define MINER_DASH_RANGE 4 - -/* - -BLOOD-DRUNK MINER - -Effectively a highly aggressive miner, the blood-drunk miner has very few attacks but compensates by being highly aggressive. - -The blood-drunk miner's attacks are as follows -- If not in KA range, it will rapidly dash at its target -- If in KA range, it will fire its kinetic accelerator -- If in melee range, will rapidly attack, akin to an actual player -- After any of these attacks, may transform its cleaving saw: - Untransformed, it attacks very rapidly for smaller amounts of damage - Transformed, it attacks at normal speed for higher damage and cleaves enemies hit - -When the blood-drunk miner dies, it leaves behind the cleaving saw it was using and its kinetic accelerator. - -Difficulty: Medium - -*/ - -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner - name = "blood-drunk miner" - desc = "A miner destined to wander forever, engaged in an endless hunt." - health = 900 - maxHealth = 900 - icon_state = "miner" - icon_living = "miner" - icon = 'icons/mob/alienqueen.dmi' - light_color = "#E4C7C5" - speak_emote = list("roars") - speed = 1 - move_to_delay = 3 - projectiletype = /obj/item/projectile/kinetic/miner - projectilesound = 'sound/weapons/kenetic_accel.ogg' - ranged = 1 - ranged_cooldown_time = 16 - pixel_x = -16 - loot = list(/obj/item/melee/energy/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator) - wander = FALSE - del_on_death = TRUE - blood_volume = BLOOD_VOLUME_NORMAL - var/obj/item/melee/energy/cleaving_saw/miner/miner_saw - var/time_until_next_transform = 0 - var/dashing = FALSE - var/dash_cooldown = 15 - var/guidance = FALSE - deathmessage = "falls to the ground, decaying into glowing particles." - death_sound = "bodyfall" - -/obj/item/gps/internal/miner - icon_state = null - gpstag = "Resonant Signal" - desc = "The sweet blood, oh, it sings to me." - invisibility = 100 - -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/guidance - guidance = TRUE - -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/hunter/AttackingTarget() - . = ..() - if(. && prob(12)) - INVOKE_ASYNC(src, .proc/dash) - -/obj/item/melee/energy/cleaving_saw/miner //nerfed saw because it is very murdery - force = 6 - force_on = 10 - -/obj/item/melee/energy/cleaving_saw/miner/attack(mob/living/target, mob/living/carbon/human/user) - target.add_stun_absorption("miner", 10, INFINITY) - ..() - target.stun_absorption -= "miner" - -/obj/item/projectile/kinetic/miner - damage = 20 - speed = 0.9 - icon_state = "ka_tracer" - range = MINER_DASH_RANGE - -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Initialize() - . = ..() - miner_saw = new(src) - internal_gps = new/obj/item/gps/internal/miner(src) - - // Add a zone selection UI; otherwise the mob can't melee attack properly. - zone_sel = new /obj/screen/zone_sel() - -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/adjustHealth(amount, updating_health = TRUE, forced = FALSE) - var/adjustment_amount = amount * 0.1 - if(world.time + adjustment_amount > next_move) - changeNext_move(adjustment_amount) //attacking it interrupts it attacking, but only briefly - . = ..() - -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/death() - if(health > 0) - return - new /obj/effect/temp_visual/dir_setting/miner_death(loc, dir) - return ..() - -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Move(atom/newloc) - if(dashing || (newloc && newloc.z == z && (islava(newloc) || ischasm(newloc)))) //we're not stupid! - return FALSE - return ..() - -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/ex_act(severity, target) - if(dash()) - return - return ..() - -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/AttackingTarget() - if(QDELETED(target)) - return - if(next_move > world.time || !Adjacent(target)) //some cheating - INVOKE_ASYNC(src, .proc/quick_attack_loop) - return - face_atom(target) - if(isliving(target)) - var/mob/living/L = target - if(L.stat == DEAD) - visible_message("[src] butchers [L]!", - "You butcher [L], restoring your health!") - if(!is_station_level(z) || client) //NPC monsters won't heal while on station - if(guidance) - adjustHealth(-L.maxHealth) - else - adjustHealth(-(L.maxHealth * 0.5)) - L.gib() - return TRUE - changeNext_move(CLICK_CD_MELEE) - miner_saw.melee_attack_chain(src, target) - if(guidance) - adjustHealth(-2) - transform_weapon() - INVOKE_ASYNC(src, .proc/quick_attack_loop) - return TRUE - -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect) - if(!used_item && !isturf(A)) - used_item = miner_saw - ..() - -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/GiveTarget(new_target) - var/targets_the_same = (new_target == target) - . = ..() - if(. && target && !targets_the_same) - wander = TRUE - transform_weapon() - INVOKE_ASYNC(src, .proc/quick_attack_loop) - -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/OpenFire() - Goto(target, move_to_delay, minimum_distance) - if(get_dist(src, target) > MINER_DASH_RANGE && dash_cooldown <= world.time) - INVOKE_ASYNC(src, .proc/dash, target) - else - shoot_ka() - transform_weapon() - -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/shoot_ka() - if(ranged_cooldown <= world.time && get_dist(src, target) <= MINER_DASH_RANGE && !Adjacent(target)) - ranged_cooldown = world.time + ranged_cooldown_time - visible_message("[src] fires the proto-kinetic accelerator!") - face_atom(target) - new /obj/effect/temp_visual/dir_setting/firing_effect(loc, dir) - Shoot(target) - changeNext_move(CLICK_CD_RANGE) - -//I'm still of the belief that this entire proc needs to be wiped from existence. -// do not take my touching of it to be endorsement of it. ~mso -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/quick_attack_loop() - while(!QDELETED(target) && next_move <= world.time) //this is done this way because next_move can change to be sooner while we sleep. - stoplag(1) - sleep((next_move - world.time) * 1.5) //but don't ask me what the fuck this is about - if(QDELETED(target)) - return - if(dashing || next_move > world.time || !Adjacent(target)) - if(dashing && next_move <= world.time) - next_move = world.time + 1 - INVOKE_ASYNC(src, .proc/quick_attack_loop) //lets try that again. - return - AttackingTarget() - -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/dash(atom/dash_target) - if(world.time < dash_cooldown) - return - var/list/accessable_turfs = list() - var/self_dist_to_target = 0 - var/turf/own_turf = get_turf(src) - if(!QDELETED(dash_target)) - self_dist_to_target += get_dist(dash_target, own_turf) - for(var/turf/simulated/O in RANGE_TURFS(MINER_DASH_RANGE, own_turf)) - var/turf_dist_to_target = 0 - if(!QDELETED(dash_target)) - turf_dist_to_target += get_dist(dash_target, O) - if(get_dist(src, O) >= MINER_DASH_RANGE && turf_dist_to_target <= self_dist_to_target && !islava(O) && !ischasm(O)) - var/valid = TRUE - for(var/turf/T in getline(own_turf, O)) - if(is_blocked_turf(T, TRUE)) - valid = FALSE - continue - if(valid) - accessable_turfs[O] = turf_dist_to_target - var/turf/target_turf - if(!QDELETED(dash_target)) - var/closest_dist = MINER_DASH_RANGE - for(var/t in accessable_turfs) - if(accessable_turfs[t] < closest_dist) - closest_dist = accessable_turfs[t] - for(var/t in accessable_turfs) - if(accessable_turfs[t] != closest_dist) - accessable_turfs -= t - if(!LAZYLEN(accessable_turfs)) - return - dash_cooldown = world.time + initial(dash_cooldown) - target_turf = pick(accessable_turfs) - var/turf/step_back_turf = get_step(target_turf, get_cardinal_dir(target_turf, own_turf)) - var/turf/step_forward_turf = get_step(own_turf, get_cardinal_dir(own_turf, target_turf)) - new /obj/effect/temp_visual/small_smoke/halfsecond(step_back_turf) - new /obj/effect/temp_visual/small_smoke/halfsecond(step_forward_turf) - var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc, src) - animate(D, alpha = 0, time = 5) - forceMove(step_back_turf) - playsound(own_turf, 'sound/weapons/punchmiss.ogg', 40, 1, -1) - dashing = TRUE - alpha = 0 - animate(src, alpha = 255, time = 5) - sleep(2) - D.forceMove(step_forward_turf) - forceMove(target_turf) - playsound(target_turf, 'sound/weapons/punchmiss.ogg', 40, 1, -1) - sleep(1) - dashing = FALSE - shoot_ka() - return TRUE - -/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/transform_weapon() - if(time_until_next_transform <= world.time) - miner_saw.transform_cooldown = 0 - miner_saw.transform_weapon(src, TRUE) - icon_state = "miner[miner_saw.active ? "_transformed":""]" - icon_living = "miner[miner_saw.active ? "_transformed":""]" - time_until_next_transform = world.time + rand(50, 100) - -/obj/effect/temp_visual/dir_setting/miner_death - icon_state = "miner_death" - duration = 15 - -/obj/effect/temp_visual/dir_setting/miner_death/Initialize(mapload, set_dir) - . = ..() - INVOKE_ASYNC(src, .proc/fade_out) - -/obj/effect/temp_visual/dir_setting/miner_death/proc/fade_out() - var/matrix/M = new - M.Turn(pick(90, 270)) - var/final_dir = dir - if(dir & (EAST|WEST)) //Facing east or west - final_dir = pick(NORTH, SOUTH) //So you fall on your side rather than your face or ass - - animate(src, transform = M, pixel_y = -6, dir = final_dir, time = 2, easing = EASE_IN|EASE_OUT) - sleep(5) - animate(src, color = list("#A7A19E", "#A7A19E", "#A7A19E", list(0, 0, 0)), time = 10, easing = EASE_IN, flags = ANIMATION_PARALLEL) - sleep(4) - animate(src, alpha = 0, time = 6, easing = EASE_OUT, flags = ANIMATION_PARALLEL) - +#define MINER_DASH_RANGE 4 + +/* + +BLOOD-DRUNK MINER + +Effectively a highly aggressive miner, the blood-drunk miner has very few attacks but compensates by being highly aggressive. + +The blood-drunk miner's attacks are as follows +- If not in KA range, it will rapidly dash at its target +- If in KA range, it will fire its kinetic accelerator +- If in melee range, will rapidly attack, akin to an actual player +- After any of these attacks, may transform its cleaving saw: + Untransformed, it attacks very rapidly for smaller amounts of damage + Transformed, it attacks at normal speed for higher damage and cleaves enemies hit + +When the blood-drunk miner dies, it leaves behind the cleaving saw it was using and its kinetic accelerator. + +Difficulty: Medium + +*/ + +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner + name = "blood-drunk miner" + desc = "A miner destined to wander forever, engaged in an endless hunt." + health = 900 + maxHealth = 900 + icon_state = "miner" + icon_living = "miner" + icon = 'icons/mob/alienqueen.dmi' + light_color = "#E4C7C5" + speak_emote = list("roars") + speed = 1 + move_to_delay = 3 + projectiletype = /obj/item/projectile/kinetic/miner + projectilesound = 'sound/weapons/kenetic_accel.ogg' + ranged = 1 + ranged_cooldown_time = 16 + pixel_x = -16 + loot = list(/obj/item/melee/energy/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator) + wander = FALSE + del_on_death = TRUE + blood_volume = BLOOD_VOLUME_NORMAL + var/obj/item/melee/energy/cleaving_saw/miner/miner_saw + var/time_until_next_transform = 0 + var/dashing = FALSE + var/dash_cooldown = 15 + var/guidance = FALSE + deathmessage = "falls to the ground, decaying into glowing particles." + death_sound = "bodyfall" + +/obj/item/gps/internal/miner + icon_state = null + gpstag = "Resonant Signal" + desc = "The sweet blood, oh, it sings to me." + invisibility = 100 + +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/guidance + guidance = TRUE + +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/hunter/AttackingTarget() + . = ..() + if(. && prob(12)) + INVOKE_ASYNC(src, .proc/dash) + +/obj/item/melee/energy/cleaving_saw/miner //nerfed saw because it is very murdery + force = 6 + force_on = 10 + +/obj/item/melee/energy/cleaving_saw/miner/attack(mob/living/target, mob/living/carbon/human/user) + target.add_stun_absorption("miner", 10, INFINITY) + ..() + target.stun_absorption -= "miner" + +/obj/item/projectile/kinetic/miner + damage = 20 + speed = 0.9 + icon_state = "ka_tracer" + range = MINER_DASH_RANGE + +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Initialize() + . = ..() + miner_saw = new(src) + internal_gps = new/obj/item/gps/internal/miner(src) + + // Add a zone selection UI; otherwise the mob can't melee attack properly. + zone_sel = new /obj/screen/zone_sel() + +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/adjustHealth(amount, updating_health = TRUE, forced = FALSE) + var/adjustment_amount = amount * 0.1 + if(world.time + adjustment_amount > next_move) + changeNext_move(adjustment_amount) //attacking it interrupts it attacking, but only briefly + . = ..() + +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/death() + if(health > 0) + return + new /obj/effect/temp_visual/dir_setting/miner_death(loc, dir) + return ..() + +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Move(atom/newloc) + if(dashing || (newloc && newloc.z == z && (islava(newloc) || ischasm(newloc)))) //we're not stupid! + return FALSE + return ..() + +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/ex_act(severity, target) + if(dash()) + return + return ..() + +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/AttackingTarget() + if(QDELETED(target)) + return + if(next_move > world.time || !Adjacent(target)) //some cheating + INVOKE_ASYNC(src, .proc/quick_attack_loop) + return + face_atom(target) + if(isliving(target)) + var/mob/living/L = target + if(L.stat == DEAD) + visible_message("[src] butchers [L]!", + "You butcher [L], restoring your health!") + if(!is_station_level(z) || client) //NPC monsters won't heal while on station + if(guidance) + adjustHealth(-L.maxHealth) + else + adjustHealth(-(L.maxHealth * 0.5)) + L.gib() + return TRUE + changeNext_move(CLICK_CD_MELEE) + miner_saw.melee_attack_chain(src, target) + if(guidance) + adjustHealth(-2) + transform_weapon() + INVOKE_ASYNC(src, .proc/quick_attack_loop) + return TRUE + +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect) + if(!used_item && !isturf(A)) + used_item = miner_saw + ..() + +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/GiveTarget(new_target) + var/targets_the_same = (new_target == target) + . = ..() + if(. && target && !targets_the_same) + wander = TRUE + transform_weapon() + INVOKE_ASYNC(src, .proc/quick_attack_loop) + +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/OpenFire() + Goto(target, move_to_delay, minimum_distance) + if(get_dist(src, target) > MINER_DASH_RANGE && dash_cooldown <= world.time) + INVOKE_ASYNC(src, .proc/dash, target) + else + shoot_ka() + transform_weapon() + +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/shoot_ka() + if(ranged_cooldown <= world.time && get_dist(src, target) <= MINER_DASH_RANGE && !Adjacent(target)) + ranged_cooldown = world.time + ranged_cooldown_time + visible_message("[src] fires the proto-kinetic accelerator!") + face_atom(target) + new /obj/effect/temp_visual/dir_setting/firing_effect(loc, dir) + Shoot(target) + changeNext_move(CLICK_CD_RANGE) + +//I'm still of the belief that this entire proc needs to be wiped from existence. +// do not take my touching of it to be endorsement of it. ~mso +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/quick_attack_loop() + while(!QDELETED(target) && next_move <= world.time) //this is done this way because next_move can change to be sooner while we sleep. + stoplag(1) + sleep((next_move - world.time) * 1.5) //but don't ask me what the fuck this is about + if(QDELETED(target)) + return + if(dashing || next_move > world.time || !Adjacent(target)) + if(dashing && next_move <= world.time) + next_move = world.time + 1 + INVOKE_ASYNC(src, .proc/quick_attack_loop) //lets try that again. + return + AttackingTarget() + +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/dash(atom/dash_target) + if(world.time < dash_cooldown) + return + var/list/accessable_turfs = list() + var/self_dist_to_target = 0 + var/turf/own_turf = get_turf(src) + if(!QDELETED(dash_target)) + self_dist_to_target += get_dist(dash_target, own_turf) + for(var/turf/simulated/O in RANGE_TURFS(MINER_DASH_RANGE, own_turf)) + var/turf_dist_to_target = 0 + if(!QDELETED(dash_target)) + turf_dist_to_target += get_dist(dash_target, O) + if(get_dist(src, O) >= MINER_DASH_RANGE && turf_dist_to_target <= self_dist_to_target && !islava(O) && !ischasm(O)) + var/valid = TRUE + for(var/turf/T in getline(own_turf, O)) + if(is_blocked_turf(T, TRUE)) + valid = FALSE + continue + if(valid) + accessable_turfs[O] = turf_dist_to_target + var/turf/target_turf + if(!QDELETED(dash_target)) + var/closest_dist = MINER_DASH_RANGE + for(var/t in accessable_turfs) + if(accessable_turfs[t] < closest_dist) + closest_dist = accessable_turfs[t] + for(var/t in accessable_turfs) + if(accessable_turfs[t] != closest_dist) + accessable_turfs -= t + if(!LAZYLEN(accessable_turfs)) + return + dash_cooldown = world.time + initial(dash_cooldown) + target_turf = pick(accessable_turfs) + var/turf/step_back_turf = get_step(target_turf, get_cardinal_dir(target_turf, own_turf)) + var/turf/step_forward_turf = get_step(own_turf, get_cardinal_dir(own_turf, target_turf)) + new /obj/effect/temp_visual/small_smoke/halfsecond(step_back_turf) + new /obj/effect/temp_visual/small_smoke/halfsecond(step_forward_turf) + var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc, src) + animate(D, alpha = 0, time = 5) + forceMove(step_back_turf) + playsound(own_turf, 'sound/weapons/punchmiss.ogg', 40, 1, -1) + dashing = TRUE + alpha = 0 + animate(src, alpha = 255, time = 5) + sleep(2) + D.forceMove(step_forward_turf) + forceMove(target_turf) + playsound(target_turf, 'sound/weapons/punchmiss.ogg', 40, 1, -1) + sleep(1) + dashing = FALSE + shoot_ka() + return TRUE + +/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/transform_weapon() + if(time_until_next_transform <= world.time) + miner_saw.transform_cooldown = 0 + miner_saw.transform_weapon(src, TRUE) + icon_state = "miner[miner_saw.active ? "_transformed":""]" + icon_living = "miner[miner_saw.active ? "_transformed":""]" + time_until_next_transform = world.time + rand(50, 100) + +/obj/effect/temp_visual/dir_setting/miner_death + icon_state = "miner_death" + duration = 15 + +/obj/effect/temp_visual/dir_setting/miner_death/Initialize(mapload, set_dir) + . = ..() + INVOKE_ASYNC(src, .proc/fade_out) + +/obj/effect/temp_visual/dir_setting/miner_death/proc/fade_out() + var/matrix/M = new + M.Turn(pick(90, 270)) + var/final_dir = dir + if(dir & (EAST|WEST)) //Facing east or west + final_dir = pick(NORTH, SOUTH) //So you fall on your side rather than your face or ass + + animate(src, transform = M, pixel_y = -6, dir = final_dir, time = 2, easing = EASE_IN|EASE_OUT) + sleep(5) + animate(src, color = list("#A7A19E", "#A7A19E", "#A7A19E", list(0, 0, 0)), time = 10, easing = EASE_IN, flags = ANIMATION_PARALLEL) + sleep(4) + animate(src, alpha = 0, time = 6, easing = EASE_OUT, flags = ANIMATION_PARALLEL) + #undef MINER_DASH_RANGE \ No newline at end of file diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index c74d7545473a..ed3f0635c1c4 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -2,6 +2,7 @@ GLOB.mob_list -= src GLOB.dead_mob_list -= src GLOB.living_mob_list -= src + focus = null QDEL_NULL(hud_used) if(mind && mind.current == src) spellremove(src) @@ -30,6 +31,7 @@ GLOB.dead_mob_list += src else GLOB.living_mob_list += src + set_focus(src) prepare_huds() ..() @@ -50,7 +52,6 @@ /mob/proc/GetAltName() return "" - /mob/proc/Cell() set category = "Admin" set hidden = 1 @@ -173,6 +174,7 @@ return 0 /mob/proc/Life(seconds, times_fired) + set waitfor = FALSE if(forced_look) if(!isnum(forced_look)) var/atom/A = locateUID(forced_look) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 38f980afdacd..a26131b95045 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -399,9 +399,6 @@ var/list/intents = list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM) set name = "Rest" set category = "IC" - if(world.time < client.move_delay) - return - if(!resting) client.move_delay = world.time + 20 to_chat(src, "You are now resting.") diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index f26961eace91..47909a5d0b92 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -15,102 +15,6 @@ return 1 return (!mover.density || !density || lying) -//The byond version of these verbs wait for the next tick before acting. -// instant verbs however can run mid tick or even during the time between ticks. -#define DO_MOVE(this_dir) var/final_dir = turn(this_dir, -dir2angle(dir)); Move(get_step(mob, final_dir), final_dir); - -/client/verb/moveup() - set name = ".moveup" - set instant = 1 - DO_MOVE(NORTH) - -/client/verb/movedown() - set name = ".movedown" - set instant = 1 - DO_MOVE(SOUTH) - -/client/verb/moveright() - set name = ".moveright" - set instant = 1 - DO_MOVE(EAST) - -/client/verb/moveleft() - set name = ".moveleft" - set instant = 1 - DO_MOVE(WEST) - -#undef DO_MOVE - -/client/Northeast() - swap_hand() - return - - -/client/Southeast() - attack_self() - return - - -/client/Southwest() - if(iscarbon(usr)) - var/mob/living/carbon/C = usr - C.toggle_throw_mode() - else if(isrobot(usr)) - var/mob/living/silicon/robot/R = usr - var/module = R.get_selected_module() - if(!module) - to_chat(usr, "You have no module selected.") - return - R.cycle_modules() - R.uneq_numbered(module) - else - to_chat(usr, "This mob type cannot throw items.") - return - - -/client/Northwest() - if(iscarbon(usr)) - var/mob/living/carbon/C = usr - if(!C.get_active_hand()) - to_chat(usr, "You have nothing to drop in your hand.") - return - drop_item() - else if(isrobot(usr)) - var/mob/living/silicon/robot/R = usr - if(!R.get_selected_module()) - to_chat(usr, "You have no module selected.") - return - R.deselect_module(R.get_selected_module()) - else - to_chat(usr, "This mob type cannot drop items.") - return - -//This gets called when you press the delete button. -/client/verb/delete_key_pressed() - set hidden = 1 - - if(!usr.pulling) - to_chat(usr, "You are not pulling anything.") - return - usr.stop_pulling() - -/client/verb/swap_hand() - set hidden = 1 - if(istype(mob, /mob/living/carbon)) - mob:swap_hand() - if(istype(mob,/mob/living/silicon/robot)) - var/mob/living/silicon/robot/R = mob - R.cycle_modules() - return - - - -/client/verb/attack_self() - set hidden = 1 - if(mob) - mob.mode() - return - /client/verb/toggle_throw_mode() set hidden = 1 @@ -128,7 +32,7 @@ return -/client/Center() +/* /client/Center() /* No 3D movement in 2D spessman game. dir 16 is Z Up if(isobj(mob.loc)) var/obj/O = mob.loc @@ -136,7 +40,7 @@ return O.relaymove(mob, 16) */ return - + */ /client/proc/Move_object(direct) @@ -155,11 +59,17 @@ /client/Move(n, direct) if(world.time < move_delay) return + else + next_move_dir_add = 0 + next_move_dir_sub = 0 var/old_move_delay = move_delay move_delay = world.time + world.tick_lag //this is here because Move() can now be called multiple times per tick if(!mob || !mob.loc) return 0 + if(!n || !direct) // why did we never check this before? + return FALSE + if(mob.notransform) return 0 //This is sota the goto stop mobs from moving var @@ -226,9 +136,9 @@ moving = 1 var/delay = mob.movement_delay() if(old_move_delay + (delay * MOVEMENT_DELAY_BUFFER_DELTA) + MOVEMENT_DELAY_BUFFER > world.time) - move_delay = old_move_delay + delay + move_delay = old_move_delay else - move_delay = delay + world.time + move_delay = world.time mob.last_movement = world.time if(locate(/obj/item/grab, mob)) @@ -264,19 +174,29 @@ return else if(mob.confused) - step(mob, pick(cardinal)) - else - . = ..() - + var/newdir = 0 + if(mob.confused > 40) + newdir = pick(alldirs) + else if(prob(mob.confused * 1.5)) + newdir = angle2dir(dir2angle(direct) + pick(90, -90)) + else if(prob(mob.confused * 3)) + newdir = angle2dir(dir2angle(direct) + pick(45, -45)) + if(newdir) + direct = newdir + n = get_step(mob, direct) + + . = ..() mob.setDir(direct) for(var/obj/item/grab/G in mob) if(G.state == GRAB_NECK) - mob.setDir(reverse_dir[direct]) + mob.setDir(angle2dir((dir2angle(direct) + 202.5) % 365)) G.adjust_position() for(var/obj/item/grab/G in mob.grabbed_by) G.adjust_position() - + if((direct & (direct - 1)) && mob.loc == n) //moved diagonally successfully + delay = mob.movement_delay() * 2 + move_delay += delay moving = 0 if(mob && .) if(mob.throwing) @@ -461,3 +381,123 @@ /mob/proc/update_gravity() return +/client/proc/check_has_body_select() + return mob && mob.hud_used && mob.zone_sel && istype(mob.zone_sel, /obj/screen/zone_sel) + +/client/verb/body_toggle_head() + set name = "body-toggle-head" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/next_in_line + switch(mob.zone_sel.selecting) + if(BODY_ZONE_HEAD) + next_in_line = BODY_ZONE_PRECISE_EYES + if(BODY_ZONE_PRECISE_EYES) + next_in_line = BODY_ZONE_PRECISE_MOUTH + else + next_in_line = BODY_ZONE_HEAD + + var/obj/screen/zone_sel/selector = mob.zone_sel + selector.set_selected_zone(next_in_line, mob) + +/client/verb/body_r_arm() + set name = "body-r-arm" + set hidden = 1 + if(!check_has_body_select()) + return + + var/next_in_line + if(mob.zone_sel.selecting == BODY_ZONE_R_ARM) + next_in_line = BODY_ZONE_PRECISE_R_HAND + else + next_in_line = BODY_ZONE_R_ARM + + var/obj/screen/zone_sel/selector = mob.zone_sel + selector.set_selected_zone(next_in_line, mob) + +/client/verb/body_chest() + set name = "body-chest" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/obj/screen/zone_sel/selector = mob.zone_sel + selector.set_selected_zone(BODY_ZONE_CHEST, mob) + +/client/verb/body_l_arm() + set name = "body-l-arm" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/next_in_line + if(mob.zone_sel.selecting == BODY_ZONE_L_ARM) + next_in_line = BODY_ZONE_PRECISE_L_HAND + else + next_in_line = BODY_ZONE_L_ARM + + var/obj/screen/zone_sel/selector = mob.zone_sel + selector.set_selected_zone(next_in_line, mob) + +/client/verb/body_r_leg() + set name = "body-r-leg" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/next_in_line + if(mob.zone_sel.selecting == BODY_ZONE_R_LEG) + next_in_line = BODY_ZONE_PRECISE_R_FOOT + else + next_in_line = BODY_ZONE_R_LEG + + var/obj/screen/zone_sel/selector = mob.zone_sel + selector.set_selected_zone(next_in_line, mob) + +/client/verb/body_groin() + set name = "body-groin" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/obj/screen/zone_sel/selector = mob.zone_sel + selector.set_selected_zone(BODY_ZONE_PRECISE_GROIN, mob) + +/client/verb/body_l_leg() + set name = "body-l-leg" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/next_in_line + if(mob.zone_sel.selecting == BODY_ZONE_L_LEG) + next_in_line = BODY_ZONE_PRECISE_L_FOOT + else + next_in_line = BODY_ZONE_L_LEG + + var/obj/screen/zone_sel/selector = mob.zone_sel + selector.set_selected_zone(next_in_line, mob) + +/client/verb/toggle_walk_run() + set name = "toggle-walk-run" + set hidden = TRUE + set instant = TRUE + if(mob) + mob.toggle_move_intent(usr) + +/mob/proc/toggle_move_intent(mob/user) + if(m_intent == MOVE_INTENT_RUN) + m_intent = MOVE_INTENT_WALK + else + m_intent = MOVE_INTENT_RUN + if(hud_used && hud_used.static_inventory) + for(var/obj/screen/mov_intent/selector in hud_used.static_inventory) + selector.update_icon(src) diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm index 8b5f64e94fbc..388b523518b1 100644 --- a/code/modules/power/gravitygenerator.dm +++ b/code/modules/power/gravitygenerator.dm @@ -46,7 +46,7 @@ var/const/GRAV_NEEDS_WRENCH = 3 // You aren't allowed to move. /obj/machinery/gravity_generator/Move() - ..() + . = ..() qdel(src) /obj/machinery/gravity_generator/proc/set_broken() diff --git a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm index c6c6de434c22..62d52c587c9b 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm @@ -144,7 +144,7 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin /obj/structure/particle_accelerator/Move() - ..() + . = ..() if(master && master.active) master.toggle_power() investigate_log("was moved whilst active; it powered down.","singulo") diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm index 16f8e8f067f6..0bc9a074179d 100644 --- a/code/modules/spacepods/spacepod.dm +++ b/code/modules/spacepods/spacepod.dm @@ -1037,7 +1037,7 @@ obj/spacepod/proc/add_equipment(mob/user, var/obj/item/spacepod_equipment/SPE, v return stop() /obj/spacepod/relaymove(mob/user, direction) - if(usr != src.pilot) + if(user != src.pilot) return handlerelaymove(user, direction) diff --git a/config/example/dbconfig.txt b/config/example/dbconfig.txt index 8b411db15e14..fc1bced9d6aa 100644 --- a/config/example/dbconfig.txt +++ b/config/example/dbconfig.txt @@ -9,7 +9,7 @@ ## This value must be set to the version of the paradise schema in use. ## If this value does not match, the SQL database will not be loaded and an error will be generated. ## Roundstart will be delayed. -DB_VERSION 6 +DB_VERSION 7 ## Server the MySQL database can be found at. # Examples: localhost, 200.135.5.43, www.mysqldb.com, etc. diff --git a/goon/browserassets/js/browserOutput.js b/goon/browserassets/js/browserOutput.js index d23b5a71ad09..1fcca3c60686 100644 --- a/goon/browserassets/js/browserOutput.js +++ b/goon/browserassets/js/browserOutput.js @@ -1046,14 +1046,6 @@ $(function() { location.reload(); }); - // Tell BYOND to give us a macro list. - // I don't know why but for some retarded reason, - // You need to activate hotkeymode before you can winget the macros in it. - runByond('byond://winset?id=mainwindow¯o=hotkeymode') - runByond('byond://winset?id=mainwindow¯o=macro') - - runByond('byond://winget?callback=wingetMacros&id=hotkeymode.*&property=command'); - /***************************************** * * KICK EVERYTHING OFF diff --git a/interface/interface.dm b/interface/interface.dm index 0bec55ef51cd..dc6a59af80b8 100644 --- a/interface/interface.dm +++ b/interface/interface.dm @@ -96,27 +96,6 @@ src << link(config.donationsurl) else to_chat(src, "The rules URL is not set in the server configuration.") - -/client/verb/hotkey_mode() - set name = "Set Hotkey Mode" - set category = "Preferences" - - var/list/hotkey_modes = list() - for(var/hotkey_mode in subtypesof(/datum/hotkey_mode)) - var/datum/hotkey_mode/H = hotkey_mode - hotkey_modes[initial(H.name)] = H - - var/chosen_mode_name = input("Choose your preferred hotkey mode.", "Hotkey mode selection") as null|anything in hotkey_modes - if(!chosen_mode_name) - return - - var/datum/hotkey_mode/chosen_mode = hotkey_modes[chosen_mode_name] - var/datum/hotkey_mode/hotkey_mode = new chosen_mode(src) - - hotkey_mode.set_winset_values() - to_chat(usr, "Your hotkey mode has been changed to [hotkey_mode.name].") - - qdel(hotkey_mode) /client/verb/hotkeys_help() set name = "Hotkey Help" @@ -124,11 +103,12 @@ var/adminhotkeys = {" Admin: -\tF5 = Asay -\tF6 = Admin Ghost -\tF7 = Player Panel -\tF8 = Admin PM -\tF9 = Invisimin +\tF3 = asay +\tF5 = Aghost (admin-ghost) +\tF6 = player-panel +\tF7 = Buildmode +\tF8 = Invisimin +\tCtrl+F8 = Stealthmin Admin ghost: \tCtrl+Click = Player Panel @@ -144,56 +124,58 @@ Admin ghost: /mob/proc/hotkey_help() var/hotkey_mode = {" Hotkey-Mode: (hotkey-mode must be on) -\tTAB = Toggle Hotkey Mode -\ta = Move Left -\ts = Move Down -\td = Move Right -\tw = Move Up -\tq = Drop Item -\te = Equip Item -\tr = Throw Item -\tm = Me -\tt = Say -\to = OOC -\tb = Resist -\tx = Swap Hands -\tz = Activate Held Object (or y) -\tf = Cycle Intents Left -\tg = Cycle Intents Right -\t1 = Help Intent -\t2 = Disarm Intent -\t3 = Grab Intent -\t4 = Harm Intent +\tTAB = toggle hotkey-mode +\tA = left +\tS = down +\tD = right +\tW = up +\tQ = drop +\tE = equip +\tR = throw +\tM = me +\tT = say +\tO = OOC +\tB = resist +\tH = Holster/unholster gun if you have a holster +\tX = swap-hand +\tZ = activate held object (or y) +\tF = cycle-intents-left +\tG = cycle-intents-right +\t1 = help-intent +\t2 = disarm-intent +\t3 = grab-intent +\t4 = harm-intent +\tNumpad = Body target selection (Press 8 repeatedly for Head->Eyes->Mouth) +\tAlt(HOLD) = Alter movement intent "} var/other = {" Any-Mode: (hotkey doesn't need to be on) -\tCtrl+a = Move Left -\tCtrl+s = Move Down -\tCtrl+d = Move Right -\tCtrl+w = Move Up -\tCtrl+q = Drop Item -\tCtrl+e = Equip Item -\tCtrl+r = Throw Item -\tCtrl+b = Resist -\tCtrl+o = OOC -\tCtrl+x = Swap Hands -\tCtrl+z = Activate Held Object (or Ctrl+y) -\tCtrl+f = Cycle Intents Left -\tCtrl+g = Cycle Intents Right -\tCtrl+1 = Help Intent -\tCtrl+2 = Disarm Intent -\tCtrl+3 = Grab Intent -\tCtrl+4 = Harm Intent -\tDEL = Pull -\tINS = Cycle Intents Right -\tHOME = Drop Item -\tPGUP = Swap Hands -\tPGDN = Activate Held Object -\tEND = Throw Item -\tF2 = OOC -\tF3 = Say -\tF4 = Me +\tCtrl+A = left +\tCtrl+S = down +\tCtrl+D = right +\tCtrl+W = up +\tCtrl+Q = drop +\tCtrl+E = equip +\tCtrl+R = throw +\tCtrl+B = resist +\tCtrl+H = stop pulling +\tCtrl+O = OOC +\tCtrl+X = swap-hand +\tCtrl+Z = activate held object (or Ctrl+y) +\tCtrl+F = cycle-intents-left +\tCtrl+G = cycle-intents-right +\tCtrl+1 = help-intent +\tCtrl+2 = disarm-intent +\tCtrl+3 = grab-intent +\tCtrl+4 = harm-intent +\tDEL = stop pulling +\tINS = cycle-intents-right +\tHOME = drop +\tPGUP = swap-hand +\tPGDN = activate held object +\tEND = throw +\tCtrl+Numpad = Body target selection (Press 8 repeatedly for Head->Eyes->Mouth) "} to_chat(src, hotkey_mode) @@ -203,19 +185,19 @@ Any-Mode: (hotkey doesn't need to be on) var/hotkey_mode = {" Hotkey-Mode: (hotkey-mode must be on) \tTAB = Toggle Hotkey Mode -\ta = Move Left -\ts = Move Down -\td = Move Right -\tw = Move Up -\tq = Unequip Active Module -\tm = Me -\tt = Say -\to = OOC -\tx = Cycle Active Modules -\tb = Resist -\tz = Activate Held Object (or y) -\tf = Cycle Intents Left -\tg = Cycle Intents Right +\tA = Move Left +\tS = Move Down +\tD = Move Right +\tW = Move Up +\tQ = Unequip Active Module +\tM = Me +\tT = Say +\tO = OOC +\tX = Cycle Active Modules +\tB = Resist +\tZ or Y = Activate Held Object +\tF = Cycle Intents Left +\tG = Cycle Intents Right \t1 = Activate Module 1 \t2 = Activate Module 2 \t3 = Activate Module 3 @@ -224,17 +206,17 @@ Hotkey-Mode: (hotkey-mode must be on) var/other = {" Any-Mode: (hotkey doesn't need to be on) -\tCtrl+a = Move Left -\tCtrl+s = Move Down -\tCtrl+d = Move Right -\tCtrl+w = Move Up -\tCtrl+q = Unequip Active Module -\tCtrl+x = Cycle Active Modules -\tCtrl+b = Resist -\tCtrl+o = OOC -\tCtrl+z = Activate Held Object (or Ctrl+y) -\tCtrl+f = Cycle Intents Left -\tCtrl+g = Cycle Intents Right +\tCtrl+A = Move Left +\tCtrl+S = Move Down +\tCtrl+D = Move Right +\tCtrl+W = Move Up +\tCtrl+Q = Unequip Active Module +\tCtrl+X = Cycle Active Modules +\tCtrl+B = Resist +\tCtrl+O = OOC +\tCtrl+Z or Ctrl+Y = Activate Held Object +\tCtrl+F = Cycle Intents Left +\tCtrl+G = Cycle Intents Right \tCtrl+1 = Activate Module 1 \tCtrl+2 = Activate Module 2 \tCtrl+3 = Activate Module 3 diff --git a/interface/skin.dmf b/interface/skin.dmf index e77ab2f9e33e..a9af84c81edb 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -1,1411 +1,40 @@ -macro "macro" - elem "MACRO-TAB" - name = "TAB" - command = ".winset \"mainwindow.macro=hotkeymode hotkey_toggle.is-checked=true mapwindow.map.focus=true input.background-color=#f0f0f0\"" - is-disabled = false - elem "MACRO-CENTER+REP" - name = "CENTER+REP" - command = ".center" - is-disabled = false - elem "MACRO-NORTHEAST" - name = "NORTHEAST" - command = ".northeast" - is-disabled = false - elem "MACRO-SOUTHEAST" - name = "SOUTHEAST" - command = ".southeast" - is-disabled = false - elem "MACRO-SOUTHWEST" - name = "SOUTHWEST" - command = ".southwest" - is-disabled = false - elem "MACRO-NORTHWEST" - name = "NORTHWEST" - command = ".northwest" - is-disabled = false - elem "MACRO-CTRL+WEST" - name = "CTRL+WEST" - command = "westface" - is-disabled = false - elem "MACRO-WEST+REP" - name = "WEST+REP" - command = ".moveleft" - is-disabled = false - elem "MACRO-CTRL+NORTH" - name = "CTRL+NORTH" - command = "northface" - is-disabled = false - elem "MACRO-NORTH+REP" - name = "NORTH+REP" - command = ".moveup" - is-disabled = false - elem "MACRO-CTRL+EAST" - name = "CTRL+EAST" - command = "eastface" - is-disabled = false - elem "MACRO-EAST+REP" - name = "EAST+REP" - command = ".moveright" - is-disabled = false - elem "MACRO-CTRL+SOUTH" - name = "CTRL+SOUTH" - command = "southface" - is-disabled = false - elem "MACRO-SOUTH+REP" - name = "SOUTH+REP" - command = ".movedown" - is-disabled = false - elem "MACRO-INSERT" - name = "INSERT" - command = "a-intent right" - is-disabled = false - elem "MACRO-DELETE" - name = "DELETE" - command = "delete-key-pressed" - is-disabled = false - elem "MACRO-CTRL+1" - name = "CTRL+1" - command = "a-intent help" - is-disabled = false - elem "MACRO-CTRL+2" - name = "CTRL+2" - command = "a-intent disarm" - is-disabled = false - elem "MACRO-CTRL+3" - name = "CTRL+3" - command = "a-intent grab" - is-disabled = false - elem "MACRO-CTRL+4" - name = "CTRL+4" - command = "a-intent harm" - is-disabled = false - elem "MACRO-CTRL+A+REP" - name = "CTRL+A+REP" - command = ".moveleft" - is-disabled = false - elem "MACRO-CTRL+D+REP" - name = "CTRL+D+REP" - command = ".moveright" - is-disabled = false - elem "MACRO-CTRL+B" - name = "CTRL+B" - command = "resist" - is-disabled = false - elem "MACRO-CTRL+E" - name = "CTRL+E" - command = "quick-equip" - is-disabled = false - elem "MACRO-CTRL+F" - name = "CTRL+F" - command = "a-intent left" - is-disabled = false - elem "MACRO-CTRL+G" - name = "CTRL+G" - command = "a-intent right" - is-disabled = false - elem "MACRO-CTRL+Q" - name = "CTRL+Q" - command = ".northwest" - is-disabled = false - elem "MACRO-CTRL+R" - name = "CTRL+R" - command = ".southwest" - is-disabled = false - elem "MACRO-CTRL+S+REP" - name = "CTRL+S+REP" - command = ".movedown" - is-disabled = false - elem "MACRO-CTRL+W+REP" - name = "CTRL+W+REP" - command = ".moveup" - is-disabled = false - elem "MACRO-CTRL+X" - name = "CTRL+X" - command = ".northeast" - is-disabled = false - elem "MACRO-CTRL+Y" - name = "CTRL+Y" - command = "Activate-Held-Object" - is-disabled = false - elem "MACRO-CTRL+Z" - name = "CTRL+Z" - command = "Activate-Held-Object" - is-disabled = false - elem "MACRO-F1" - name = "F1" - command = "adminhelp" - is-disabled = false - elem "MACRO-CTRL+SHIFT+F1+REP" - name = "CTRL+SHIFT+F1+REP" - command = ".options" - is-disabled = false - elem "MACRO-F2" - name = "F2" - command = "ooc" - is-disabled = false - elem "MACRO-F2+REP" - name = "F2+REP" - command = ".screenshot auto" - is-disabled = false - elem "MACRO-SHIFT+F2+REP" - name = "SHIFT+F2+REP" - command = ".screenshot" - is-disabled = false - elem "MACRO-F3" - name = "F3" - command = ".say" - is-disabled = false - elem "MACRO-F4" - name = "F4" - command = ".me" - is-disabled = false - elem "MACRO-F5" - name = "F5" - command = "asay" - is-disabled = false - elem "MACRO-F6" - name = "F6" - command = "Aghost" - is-disabled = false - elem "MACRO-F7" - name = "F7" - command = "player-panel-new" - is-disabled = false - elem "MACRO-F8" - name = "F8" - command = "admin-pm-key" - is-disabled = false - elem "MACRO-F9" - name = "F9" - command = "Invisimin" - is-disabled = false - elem "MACRO-F12" - name = "F12" - command = "F12" - is-disabled = false - -macro "hotkeymode" - elem "HKMODE-TAB" - name = "TAB" - command = ".winset \"mainwindow.macro=macro hotkey_toggle.is-checked=false input.focus=true input.background-color=#d3b5b5\"" - is-disabled = false - elem "HKMODE-CENTER+REP" - name = "CENTER+REP" - command = ".center" - is-disabled = false - elem "HKMODE-NORTHEAST" - name = "NORTHEAST" - command = ".northeast" - is-disabled = false - elem "HKMODE-SOUTHEAST" - name = "SOUTHEAST" - command = ".southeast" - is-disabled = false - elem "HKMODE-SOUTHWEST" - name = "SOUTHWEST" - command = ".southwest" - is-disabled = false - elem "HKMODE-NORTHWEST" - name = "NORTHWEST" - command = ".northwest" - is-disabled = false - elem "HKMODE-CTRL+WEST" - name = "CTRL+WEST" - command = "westface" - is-disabled = false - elem "HKMODE-WEST+REP" - name = "WEST+REP" - command = ".moveleft" - is-disabled = false - elem "HKMODE-CTRL+NORTH" - name = "CTRL+NORTH" - command = "northface" - is-disabled = false - elem "HKMODE-NORTH+REP" - name = "NORTH+REP" - command = ".moveup" - is-disabled = false - elem "HKMODE-CTRL+EAST" - name = "CTRL+EAST" - command = "eastface" - is-disabled = false - elem "HKMODE-EAST+REP" - name = "EAST+REP" - command = ".moveright" - is-disabled = false - elem "HKMODE-CTRL+SOUTH" - name = "CTRL+SOUTH" - command = "southface" - is-disabled = false - elem "HKMODE-SOUTH+REP" - name = "SOUTH+REP" - command = ".movedown" - is-disabled = false - elem "HKMODE-INSERT" - name = "INSERT" - command = "a-intent right" - is-disabled = false - elem "HKMODE-DELETE" - name = "DELETE" - command = "delete-key-pressed" - is-disabled = false - elem "HKMODE-1" - name = "1" - command = "a-intent help" - is-disabled = false - elem "HKMODE-CTRL+1" - name = "CTRL+1" - command = "a-intent help" - is-disabled = false - elem "HKMODE-2" - name = "2" - command = "a-intent disarm" - is-disabled = false - elem "HKMODE-CTRL+2" - name = "CTRL+2" - command = "a-intent disarm" - is-disabled = false - elem "HKMODE-3" - name = "3" - command = "a-intent grab" - is-disabled = false - elem "HKMODE-CTRL+3" - name = "CTRL+3" - command = "a-intent grab" - is-disabled = false - elem "HKMODE-4" - name = "4" - command = "a-intent harm" - is-disabled = false - elem "HKMODE-CTRL+4" - name = "CTRL+4" - command = "a-intent harm" - is-disabled = false - elem "HKMODE-T" - name = "T" - command = ".say" - is-disabled = false - elem "HKMODE-O" - name = "O" - command = "ooc" - is-disabled = false - elem "HKMODE-M" - name = "M" - command = ".me" - is-disabled = false - elem "HKMODE-A+REP" - name = "A+REP" - command = ".moveleft" - is-disabled = false - elem "HKMODE-CTRL+A+REP" - name = "CTRL+A+REP" - command = ".moveleft" - is-disabled = false - elem "HKMODE-D+REP" - name = "D+REP" - command = ".moveright" - is-disabled = false - elem "HKMODE-CTRL+D+REP" - name = "CTRL+D+REP" - command = ".moveright" - is-disabled = false - elem "HKMODE-B" - name = "B" - command = "resist" - is-disabled = false - elem "HKMODE-CTRL+B" - name = "CTRL+B" - command = "resist" - is-disabled = false - elem "HKMODE-E" - name = "E" - command = "quick-equip" - is-disabled = false - elem "HKMODE-CTRL+E" - name = "CTRL+E" - command = "quick-equip" - is-disabled = false - elem "HKMODE-F" - name = "F" - command = "a-intent left" - is-disabled = false - elem "HKMODE-CTRL+F" - name = "CTRL+F" - command = "a-intent left" - is-disabled = false - elem "HKMODE-G" - name = "G" - command = "a-intent right" - is-disabled = false - elem "HKMODE-CTRL+G" - name = "CTRL+G" - command = "a-intent right" - is-disabled = false - elem "HKMODE-H" - name = "H" - command = "holster" - is-disabled = false - elem "HKMODE-CTRL+H" - name = "CTRL+H" - command = "holster" - is-disabled = false - elem "HKMODE-Q" - name = "Q" - command = ".northwest" - is-disabled = false - elem "HKMODE-CTRL+Q" - name = "CTRL+Q" - command = ".northwest" - is-disabled = false - elem "HKMODE-R" - name = "R" - command = ".southwest" - is-disabled = false - elem "HKMODE-CTRL+R" - name = "CTRL+R" - command = ".southwest" - is-disabled = false - elem "s_key" - name = "S+REP" - command = ".movedown" - is-disabled = false - elem "HKMODE-CTRL+S+REP" - name = "CTRL+S+REP" - command = ".movedown" - is-disabled = false - elem "HKMODE-T" - name = "T" - command = ".say" - is-disabled = false - elem "w_key" - name = "W+REP" - command = ".moveup" - is-disabled = false - elem "HKMODE-CTRL+W+REP" - name = "CTRL+W+REP" - command = ".moveup" - is-disabled = false - elem "HKMODE-X" - name = "X" - command = ".northeast" - is-disabled = false - elem "HKMODE-CTRL+X" - name = "CTRL+X" - command = ".northeast" - is-disabled = false - elem "HKMODE-Y" - name = "Y" - command = "Activate-Held-Object" - is-disabled = false - elem "HKMODE-CTRL+Y" - name = "CTRL+Y" - command = "Activate-Held-Object" - is-disabled = false - elem "HKMODE-Z" - name = "Z" - command = "Activate-Held-Object" - is-disabled = false - elem "HKMODE-CTRL+Z" - name = "CTRL+Z" - command = "Activate-Held-Object" - is-disabled = false - elem "HKMODE-F1" - name = "F1" - command = "adminhelp" - is-disabled = false - elem "HKMODE-CTRL+SHIFT+F1+REP" - name = "CTRL+SHIFT+F1+REP" - command = ".options" - is-disabled = false - elem "HKMODE-F2" - name = "F2" - command = "ooc" - is-disabled = false - elem "HKMODE-F2+REP" - name = "F2+REP" - command = ".screenshot auto" - is-disabled = false - elem "HKMODE-SHIFT+F2+REP" - name = "SHIFT+F2+REP" - command = ".screenshot" - is-disabled = false - elem "HKMODE-F3" - name = "F3" - command = ".say" - is-disabled = false - elem "HKMODE-F4" - name = "F4" - command = ".me" - is-disabled = false - elem "HKMODE-F5" - name = "F5" - command = "asay" - is-disabled = false - elem "HKMODE-F6" - name = "F6" - command = "Aghost" - is-disabled = false - elem "HKMODE-F7" - name = "F7" - command = "player-panel-new" - is-disabled = false - elem "HKMODE-F8" - name = "F8" - command = "admin-pm-key" - is-disabled = false - elem "HKMODE-F9" - name = "F9" - command = "Invisimin" - is-disabled = false - elem "HKMODE-F12" - name = "F12" - command = "F12" - is-disabled = false - -macro "borgmacro" - elem "BRGMACRO-TAB" - name = "TAB" - command = ".winset \"mainwindow.macro=borghotkeymode hotkey_toggle.is-checked=true mapwindow.map.focus=true input.background-color=#F0F0F0\"" - is-disabled = false - elem "BRGMACRO-CENTER+REP" - name = "CENTER+REP" - command = ".center" - is-disabled = false - elem "BRGMACRO-NORTHEAST" - name = "NORTHEAST" - command = ".northeast" - is-disabled = false - elem "BRGMACRO-SOUTHEAST" - name = "SOUTHEAST" - command = ".southeast" - is-disabled = false - elem "BRGMACRO-NORTHWEST" - name = "NORTHWEST" - command = "unequip-module" - is-disabled = false - elem "BRGMACRO-CTRL+WEST" - name = "CTRL+WEST" - command = "westface" - is-disabled = false - elem "BRGMACRO-WEST+REP" - name = "WEST+REP" - command = ".moveleft" - is-disabled = false - elem "BRGMACRO-CTRL+NORTH" - name = "CTRL+NORTH" - command = "northface" - is-disabled = false - elem "BRGMACRO-NORTH+REP" - name = "NORTH+REP" - command = ".moveup" - is-disabled = false - elem "BRGMACRO-CTRL+EAST" - name = "CTRL+EAST" - command = "eastface" - is-disabled = false - elem "BRGMACRO-EAST+REP" - name = "EAST+REP" - command = ".moveright" - is-disabled = false - elem "BRGMACRO-CTRL+SOUTH" - name = "CTRL+SOUTH" - command = "southface" - is-disabled = false - elem "BRGMACRO-SOUTH+REP" - name = "SOUTH+REP" - command = ".movedown" - is-disabled = false - elem "BRGMACRO-INSERT" - name = "INSERT" - command = "a-intent right" - is-disabled = false - elem "BRGMACRO-DELETE" - name = "DELETE" - command = "delete-key-pressed" - is-disabled = false - elem "BRGMACRO-CTRL+1" - name = "CTRL+1" - command = "toggle-module 1" - is-disabled = false - elem "BRGMACRO-CTRL+2" - name = "CTRL+2" - command = "toggle-module 2" - is-disabled = false - elem "BRGMACRO-CTRL+3" - name = "CTRL+3" - command = "toggle-module 3" - is-disabled = false - elem "BRGMACRO-CTRL+4" - name = "CTRL+4" - command = "a-intent left" - is-disabled = false - elem "BRGMACRO-CTRL+A+REP" - name = "CTRL+A+REP" - command = ".moveleft" - is-disabled = false - elem "BRGMACRO-CTRL+B" - name = "CTRL+B" - command = "resist" - is-disabled = false - elem "BRGMACRO-CTRL+O" - name = "CTRL+O" - command = "ooc" - is-disabled = false - elem "BRGMACRO-CTRL+D+REP" - name = "CTRL+D+REP" - command = ".moveright" - is-disabled = false - elem "BRGMACRO-CTRL+F" - name = "CTRL+F" - command = "a-intent left" - is-disabled = false - elem "BRGMACRO-CTRL+G" - name = "CTRL+G" - command = "a-intent right" - is-disabled = false - elem "BRGMACRO-CTRL+Q" - name = "CTRL+Q" - command = "unequip-module" - is-disabled = false - elem "BRGMACRO-CTRL+S+REP" - name = "CTRL+S+REP" - command = ".movedown" - is-disabled = false - elem "BRGMACRO-CTRL+W+REP" - name = "CTRL+W+REP" - command = ".moveup" - is-disabled = false - elem "BRGMACRO-CTRL+X" - name = "CTRL+X" - command = ".northeast" - is-disabled = false - elem "BRGMACRO-CTRL+Y" - name = "CTRL+Y" - command = "Activate-Held-Object" - is-disabled = false - elem "BRGMACRO-CTRL+Z" - name = "CTRL+Z" - command = "Activate-Held-Object" - is-disabled = false - elem "BRGMACRO-F1" - name = "F1" - command = "adminhelp" - is-disabled = false - elem "BRGMACRO-CTRL+SHIFT+F1+REP" - name = "CTRL+SHIFT+F1+REP" - command = ".options" - is-disabled = false - elem "BRGMACRO-F2" - name = "F2" - command = "ooc" - is-disabled = false - elem "BRGMACRO-F2+REP" - name = "F2+REP" - command = ".screenshot auto" - is-disabled = false - elem "BRGMACRO-SHIFT+F2+REP" - name = "SHIFT+F2+REP" - command = ".screenshot" - is-disabled = false - elem "BRGMACRO-F3" - name = "F3" - command = ".say" - is-disabled = false - elem "BRGMACRO-F4" - name = "F4" - command = ".me" - is-disabled = false - elem "BRGMACRO-F5" - name = "F5" - command = "asay" - is-disabled = false - elem "BRGMACRO-F6" - name = "F6" - command = "Aghost" - is-disabled = false - elem "BRGMACRO-F7" - name = "F7" - command = "player-panel-new" - is-disabled = false - elem "BRGMACRO-F8" - name = "F8" - command = "admin-pm-key" - is-disabled = false - elem "BRGMACRO-F9" - name = "F9" - command = "Invisimin" - is-disabled = false - elem "BRGMACRO-F12" - name = "F12" - command = "F12" - is-disabled = false - -macro "borghotkeymode" - elem "BRGHK-TAB" - name = "TAB" - command = ".winset \"mainwindow.macro=borgmacro hotkey_toggle.is-checked=false input.focus=true input.background-color=#d3b5b5\"" - is-disabled = false - elem "BRGHK-CENTER+REP" - name = "CENTER+REP" - command = ".center" - is-disabled = false - elem "BRGHK-NORTHEAST" - name = "NORTHEAST" - command = ".northeast" - is-disabled = false - elem "BRGHK-SOUTHEAST" - name = "SOUTHEAST" - command = ".southeast" - is-disabled = false - elem "BRGHK-NORTHWEST" - name = "NORTHWEST" - command = "unequip-module" - is-disabled = false - elem "BRGHK-CTRL+WEST" - name = "CTRL+WEST" - command = "westface" - is-disabled = false - elem "BRGHK-WEST+REP" - name = "WEST+REP" - command = ".moveleft" - is-disabled = false - elem "BRGHK-CTRL+NORTH" - name = "CTRL+NORTH" - command = "northface" - is-disabled = false - elem "BRGHK-NORTH+REP" - name = "NORTH+REP" - command = ".moveup" - is-disabled = false - elem "BRGHK-CTRL+EAST" - name = "CTRL+EAST" - command = "eastface" - is-disabled = false - elem "BRGHK-EAST+REP" - name = "EAST+REP" - command = ".moveright" - is-disabled = false - elem "BRGHK-CTRL+SOUTH" - name = "CTRL+SOUTH" - command = "southface" - is-disabled = false - elem "BRGHK-SOUTH+REP" - name = "SOUTH+REP" - command = ".movedown" - is-disabled = false - elem "BRGHK-INSERT" - name = "INSERT" - command = "a-intent right" - is-disabled = false - elem "BRGHK-DELETE" - name = "DELETE" - command = "delete-key-pressed" - is-disabled = false - elem "BRGHK-1" - name = "1" - command = "toggle-module 1" - is-disabled = false - elem "BRGHK-CTRL+1" - name = "CTRL+1" - command = "toggle-module 1" - is-disabled = false - elem "BRGHK-2" - name = "2" - command = "toggle-module 2" - is-disabled = false - elem "BRGHK-CTRL+2" - name = "CTRL+2" - command = "toggle-module 2" - is-disabled = false - elem "BRGHK-3" - name = "3" - command = "toggle-module 3" - is-disabled = false - elem "BRGHK-CTRL+3" - name = "CTRL+3" - command = "toggle-module 3" - is-disabled = false - elem "BRGHK-4" - name = "4" - command = "a-intent left" - is-disabled = false - elem "BRGHK-CTRL+4" - name = "CTRL+4" - command = "a-intent left" - is-disabled = false - elem "BRGHK-A+REP" - name = "A+REP" - command = ".moveleft" - is-disabled = false - elem "BRGHK-CTRL+A+REP" - name = "CTRL+A+REP" - command = ".moveleft" - is-disabled = false - elem "BRGHK-B" - name = "B" - command = "resist" - is-disabled = false - elem "BRGHK-CTRL+B" - name = "CTRL+B" - command = "resist" - is-disabled = false - elem "BRGHK-T" - name = "T" - command = ".say" - is-disabled = false - elem "BRGHK-O" - name = "O" - command = "ooc" - is-disabled = false - elem "BRGHK-M" - name = "M" - command = ".me" - is-disabled = false - elem "BRGHK-CTRL+O" - name = "CTRL+O" - command = "ooc" - is-disabled = false - elem "BRGHK-D+REP" - name = "D+REP" - command = ".moveright" - is-disabled = false - elem "BRGHK-CTRL+D+REP" - name = "CTRL+D+REP" - command = ".moveright" - is-disabled = false - elem "BRGHK-F" - name = "F" - command = "a-intent left" - is-disabled = false - elem "BRGHK-CTRL+F" - name = "CTRL+F" - command = "a-intent left" - is-disabled = false - elem "BRGHK-G" - name = "G" - command = "a-intent right" - is-disabled = false - elem "BRGHK-CTRL+G" - name = "CTRL+G" - command = "a-intent right" - is-disabled = false - elem "BRGHK-Q" - name = "Q" - command = "unequip-module" - is-disabled = false - elem "BRGHK-CTRL+Q" - name = "CTRL+Q" - command = "unequip-module" - is-disabled = false - elem "s_key" - name = "S+REP" - command = ".movedown" - is-disabled = false - elem "BRGHK-CTRL+S+REP" - name = "CTRL+S+REP" - command = ".movedown" - is-disabled = false - elem "w_key" - name = "W+REP" - command = ".moveup" - is-disabled = false - elem "BRGHK-CTRL+W+REP" - name = "CTRL+W+REP" - command = ".moveup" - is-disabled = false - elem "BRGHK-X" - name = "X" - command = ".northeast" - is-disabled = false - elem "BRGHK-CTRL+X" - name = "CTRL+X" - command = ".northeast" - is-disabled = false - elem "BRGHK-Y" - name = "Y" - command = "Activate-Held-Object" - is-disabled = false - elem "BRGHK-CTRL+Y" - name = "CTRL+Y" - command = "Activate-Held-Object" - is-disabled = false - elem "BRGHK-Z" - name = "Z" - command = "Activate-Held-Object" - is-disabled = false - elem "BRGHK-CTRL+Z" - name = "CTRL+Z" - command = "Activate-Held-Object" - is-disabled = false - elem "BRGHK-F1" - name = "F1" - command = "adminhelp" - is-disabled = false - elem "BRGHK-CTRL+SHIFT+F1+REP" - name = "CTRL+SHIFT+F1+REP" - command = ".options" - is-disabled = false - elem "BRGHK-F2" - name = "F2" - command = "ooc" - is-disabled = false - elem "BRGHK-F2+REP" - name = "F2+REP" - command = ".screenshot auto" - is-disabled = false - elem "BRGHK-SHIFT+F2+REP" - name = "SHIFT+F2+REP" - command = ".screenshot" - is-disabled = false - elem "BRGHK-F3" - name = "F3" - command = ".say" - is-disabled = false - elem "BRGHK-F4" - name = "F4" - command = ".me" - is-disabled = false - elem "BRGHK-F5" - name = "F5" - command = "asay" - is-disabled = false - elem "BRGHK-F6" - name = "F6" - command = "Aghost" - is-disabled = false - elem "BRGHK-F7" - name = "F7" - command = "player-panel-new" - is-disabled = false - elem "BRGHK-F8" - name = "F8" - command = "admin-pm-key" - is-disabled = false - elem "BRGHK-F9" - name = "F9" - command = "Invisimin" - is-disabled = false - elem "BRGHK-F12" - name = "F12" - command = "F12" - is-disabled = false - -macro "azertymacro" - elem "AZRFF-TAB" - name = "TAB" - command = ".winset \"mainwindow.macro=azertyhotkeymode hotkey_toggle.is-checked=true mapwindow.map.focus=true input.background-color=#F0F0F0\"" - is-disabled = false - elem "AZRFF-CENTER+REP" - name = "CENTER+REP" - command = ".center" - is-disabled = false - elem "AZRFF-NORTHEAST" - name = "NORTHEAST" - command = ".northeast" - is-disabled = false - elem "AZRFF-SOUTHEAST" - name = "SOUTHEAST" - command = ".southeast" - is-disabled = false - elem "AZRFF-SOUTHWEST" - name = "SOUTHWEST" - command = ".southwest" - is-disabled = false - elem "AZRFF-NORTHWEST" - name = "NORTHWEST" - command = ".northwest" - is-disabled = false - elem "AZRFF-CTRL+WEST" - name = "CTRL+WEST" - command = "westface" - is-disabled = false - elem "AZRFF-WEST+REP" - name = "WEST+REP" - command = ".moveleft" - is-disabled = false - elem "AZRFF-CTRL+NORTH" - name = "CTRL+NORTH" - command = "northface" - is-disabled = false - elem "AZRFF-NORTH+REP" - name = "NORTH+REP" - command = ".moveup" - is-disabled = false - elem "AZRFF-CTRL+EAST" - name = "CTRL+EAST" - command = "eastface" - is-disabled = false - elem "AZRFF-EAST+REP" - name = "EAST+REP" - command = ".moveright" - is-disabled = false - elem "AZRFF-CTRL+SOUTH" - name = "CTRL+SOUTH" - command = "southface" - is-disabled = false - elem "AZRFF-SOUTH+REP" - name = "SOUTH+REP" - command = ".movedown" - is-disabled = false - elem "AZRFF-INSERT" - name = "INSERT" - command = "a-intent right" - is-disabled = false - elem "AZRFF-DELETE" - name = "DELETE" - command = "delete-key-pressed" - is-disabled = false - elem "AZRFF-CTRL+1" - name = "CTRL+1" - command = "a-intent help" - is-disabled = false - elem "AZRFF-CTRL+2" - name = "CTRL+2" - command = "a-intent disarm" - is-disabled = false - elem "AZRFF-CTRL+3" - name = "CTRL+3" - command = "a-intent grab" - is-disabled = false - elem "AZRFF-CTRL+4" - name = "CTRL+4" - command = "a-intent harm" - is-disabled = false - elem "AZRFF-CTRL+A" - name = "CTRL+A" - command = ".northwest" - is-disabled = false - elem "AZRFF-CTRL+D+REP" - name = "CTRL+D+REP" - command = ".moveright" - is-disabled = false - elem "AZRFF-CTRL+B" - name = "CTRL+B" - command = "resist" - is-disabled = false - elem "AZRFF-CTRL+E" - name = "CTRL+E" - command = "quick-equip" - is-disabled = false - elem "AZRFF-CTRL+F" - name = "CTRL+F" - command = "a-intent left" - is-disabled = false - elem "AZRFF-CTRL+G" - name = "CTRL+G" - command = "a-intent right" - is-disabled = false - elem "AZRFF-CTRL+Q+REP" - name = "CTRL+Q+REP" - command = ".moveleft" - is-disabled = false - elem "AZRFF-CTRL+R" - name = "CTRL+R" - command = ".southwest" - is-disabled = false - elem "AZRFF-CTRL+S+REP" - name = "CTRL+S+REP" - command = ".movedown" - is-disabled = false - elem "AZRFF-CTRL+W" - name = "CTRL+W" - command = "Activate-Held-Object" - is-disabled = false - elem "AZRFF-CTRL+X" - name = "CTRL+X" - command = ".northeast" - is-disabled = false - elem "AZRFF-CTRL+Y" - name = "CTRL+Y" - command = "Activate-Held-Object" - is-disabled = false - elem "AZRFF-CTRL+Z+REP" - name = "CTRL+Z+REP" - command = ".moveup" - is-disabled = false - elem "AZRFF-F1" - name = "F1" - command = "adminhelp" - is-disabled = false - elem "AZRFF-CTRL+SHIFT+F1+REP" - name = "CTRL+SHIFT+F1+REP" - command = ".options" - is-disabled = false - elem "AZRFF-F2" - name = "F2" - command = "ooc" - is-disabled = false - elem "AZRFF-F2+REP" - name = "F2+REP" - command = ".screenshot auto" - is-disabled = false - elem "AZRFF-SHIFT+F2+REP" - name = "SHIFT+F2+REP" - command = ".screenshot" - is-disabled = false - elem "AZRFF-F3" - name = "F3" - command = ".say" - is-disabled = false - elem "AZRFF-F4" - name = "F4" - command = ".me" - is-disabled = false - elem "AZRFF-F5" - name = "F5" - command = "asay" - is-disabled = false - elem "AZRFF-F6" - name = "F6" - command = "Aghost" - is-disabled = false - elem "AZRFF-F7" - name = "F7" - command = "player-panel-new" - is-disabled = false - elem "AZRFF-F8" - name = "F8" - command = "admin-pm-key" - is-disabled = false - elem "AZRFF-F9" - name = "F9" - command = "Invisimin" - is-disabled = false - elem "AZRFF-F12" - name = "F12" - command = "F12" - is-disabled = false - -macro "azertyhotkeymode" - elem "AZRON-TAB" - name = "TAB" - command = ".winset \"mainwindow.macro=azertymacro hotkey_toggle.is-checked=false input.focus=true input.background-color=#d3b5b5\"" - is-disabled = false - elem "AZRON-CENTER+REP" - name = "CENTER+REP" - command = ".center" - is-disabled = false - elem "AZRON-NORTHEAST" - name = "NORTHEAST" - command = ".northeast" - is-disabled = false - elem "AZRON-SOUTHEAST" - name = "SOUTHEAST" - command = ".southeast" - is-disabled = false - elem "AZRON-SOUTHWEST" - name = "SOUTHWEST" - command = ".southwest" - is-disabled = false - elem "AZRON-NORTHWEST" - name = "NORTHWEST" - command = ".northwest" - is-disabled = false - elem "AZRON-CTRL+WEST" - name = "CTRL+WEST" - command = "westface" - is-disabled = false - elem "AZRON-WEST+REP" - name = "WEST+REP" - command = ".moveleft" - is-disabled = false - elem "AZRON-CTRL+NORTH" - name = "CTRL+NORTH" - command = "northface" - is-disabled = false - elem "AZRON-NORTH+REP" - name = "NORTH+REP" - command = ".moveup" - is-disabled = false - elem "AZRON-CTRL+EAST" - name = "CTRL+EAST" - command = "eastface" - is-disabled = false - elem "AZRON-EAST+REP" - name = "EAST+REP" - command = ".moveright" - is-disabled = false - elem "AZRON-CTRL+SOUTH" - name = "CTRL+SOUTH" - command = "southface" - is-disabled = false - elem "AZRON-SOUTH+REP" - name = "SOUTH+REP" - command = ".movedown" - is-disabled = false - elem "AZRON-INSERT" - name = "INSERT" - command = "a-intent right" - is-disabled = false - elem "AZRON-DELETE" - name = "DELETE" - command = "delete-key-pressed" - is-disabled = false - elem "AZRON-1" - name = "1" - command = "a-intent help" - is-disabled = false - elem "AZRON-CTRL+1" - name = "CTRL+1" - command = "a-intent help" - is-disabled = false - elem "AZRON-2" - name = "2" - command = "a-intent disarm" - is-disabled = false - elem "AZRON-CTRL+2" - name = "CTRL+2" - command = "a-intent disarm" - is-disabled = false - elem "AZRON-3" - name = "3" - command = "a-intent grab" - is-disabled = false - elem "AZRON-CTRL+3" - name = "CTRL+3" - command = "a-intent grab" - is-disabled = false - elem "AZRON-4" - name = "4" - command = "a-intent harm" - is-disabled = false - elem "AZRON-CTRL+4" - name = "CTRL+4" - command = "a-intent harm" - is-disabled = false - elem "AZRON-A" - name = "A" - command = ".northwest" - is-disabled = false - elem "AZRON-CTRL+A" - name = "CTRL+A" - command = ".northwest" - is-disabled = false - elem "AZRON-D+REP" - name = "D+REP" - command = ".moveright" - is-disabled = false - elem "AZRON-CTRL+D+REP" - name = "CTRL+D+REP" - command = ".moveright" - is-disabled = false - elem "AZRON-CTRL+B" - name = "CTRL+B" - command = "resist" - is-disabled = false - elem "AZRON-E" - name = "E" - command = "quick-equip" - is-disabled = false - elem "AZRON-CTRL+E" - name = "CTRL+E" - command = "quick-equip" - is-disabled = false - elem "AZRON-F" - name = "F" - command = "a-intent left" - is-disabled = false - elem "AZRON-CTRL+F" - name = "CTRL+F" - command = "a-intent left" - is-disabled = false - elem "AZRON-G" - name = "G" - command = "a-intent right" - is-disabled = false - elem "AZRON-CTRL+G" - name = "CTRL+G" - command = "a-intent right" - is-disabled = false - elem "AZRON-H" - name = "H" - command = "holster" - is-disabled = false - elem "AZRON-CTRL+H" - name = "CTRL+H" - command = "holster" - is-disabled = false - elem "AZRON-Q+REP" - name = "Q+REP" - command = ".moveleft" - is-disabled = false - elem "AZRON-CTRL+Q+REP" - name = "CTRL+Q+REP" - command = ".moveleft" - is-disabled = false - elem "AZRON-R" - name = "R" - command = ".southwest" - is-disabled = false - elem "AZRON-CTRL+R" - name = "CTRL+R" - command = ".southwest" - is-disabled = false - elem "s_key" - name = "S+REP" - command = ".movedown" - is-disabled = false - elem "AZRON-CTRL+S+REP" - name = "CTRL+S+REP" - command = ".movedown" - is-disabled = false - elem "AZRON-T" - name = "T" - command = ".say" - is-disabled = false - elem "AZRON-O" - name = "O" - command = "ooc" - is-disabled = false - elem "AZRON-M" - name = "M" - command = ".me" - is-disabled = false - elem "AZRON-W" - name = "W" - command = "Activate-Held-Object" - is-disabled = false - elem "AZRON-CTRL+W" - name = "CTRL+W" - command = "Activate-Held-Object" - is-disabled = false - elem "AZRON-X" - name = "X" - command = ".northeast" - is-disabled = false - elem "AZRON-CTRL+X" - name = "CTRL+X" - command = ".northeast" - is-disabled = false - elem "AZRON-Y" - name = "Y" - command = "Activate-Held-Object" - is-disabled = false - elem "AZRON-CTRL+Y" - name = "CTRL+Y" - command = "Activate-Held-Object" - is-disabled = false - elem "w_key" - name = "Z+REP" - command = ".moveup" - is-disabled = false - elem "AZRON-CTRL+Z+REP" - name = "CTRL+Z+REP" - command = ".moveup" - is-disabled = false - elem "AZRON-F1" - name = "F1" - command = "adminhelp" - is-disabled = false - elem "AZRON-CTRL+SHIFT+F1+REP" - name = "CTRL+SHIFT+F1+REP" - command = ".options" - is-disabled = false - elem "AZRON-F2" - name = "F2" - command = "ooc" - is-disabled = false - elem "AZRON-F2+REP" - name = "F2+REP" - command = ".screenshot auto" - is-disabled = false - elem "AZRON-SHIFT+F2+REP" - name = "SHIFT+F2+REP" - command = ".screenshot" - is-disabled = false - elem "AZRON-F3" - name = "F3" - command = ".say" - is-disabled = false - elem "AZRON-F4" - name = "F4" - command = ".me" - is-disabled = false - elem "AZRON-F5" - name = "F5" - command = "asay" - is-disabled = false - elem "AZRON-F6" - name = "F6" - command = "Aghost" - is-disabled = false - elem "AZRON-F7" - name = "F7" - command = "player-panel-new" - is-disabled = false - elem "AZRON-F8" - name = "F8" - command = "admin-pm-key" - is-disabled = false - elem "AZRON-F9" - name = "F9" - command = "Invisimin" - is-disabled = false - elem "AZRON-F12" - name = "F12" - command = "F12" - is-disabled = false +macro "default" menu "menu" - elem + elem name = "&File" command = "" - category = "" - is-checked = false - can-check = false - group = "" - is-disabled = false saved-params = "is-checked" - elem + elem name = "&Quick screenshot\tF2" command = ".screenshot auto" category = "&File" - is-checked = false - can-check = false - group = "" - is-disabled = false saved-params = "is-checked" - elem + elem name = "&Save screenshot as...\tShift+F2" command = ".screenshot" category = "&File" - is-checked = false - can-check = false - group = "" - is-disabled = false saved-params = "is-checked" elem "reconnectbutton" name = "&Reconnect" command = ".reconnect" category = "&File" - is-checked = false - can-check = false - group = "" - is-disabled = false saved-params = "is-checked" - elem + elem name = "" command = "" category = "&File" - is-checked = false - can-check = false - group = "" - is-disabled = false saved-params = "is-checked" - elem + elem name = "&Quit" command = ".quit" category = "&File" - is-checked = false - can-check = false - group = "" - is-disabled = false saved-params = "is-checked" - elem + elem name = "&Icons" command = "" saved-params = "is-checked" - elem + elem name = "&Size" command = "" category = "&Icons" @@ -1453,7 +82,7 @@ menu "menu" can-check = true group = "size" saved-params = "is-checked" - elem + elem name = "&Scaling" command = "" category = "&Icons" @@ -1484,106 +113,79 @@ menu "menu" command = ".winset \"menu.textmode.is-checked=true?mapwindow.map.text-mode=true:mapwindow.map.text-mode=false\"" category = "&Icons" can-check = true - group = "" saved-params = "is-checked" - - elem + elem name = "&Help" command = "" - category = "" - is-checked = false - can-check = false - group = "" - is-disabled = false saved-params = "is-checked" - elem + elem name = "&Admin help\tF1" command = "adminhelp" category = "&Help" - is-checked = false - can-check = false - group = "" - is-disabled = false saved-params = "is-checked" - elem + elem name = "&Hotkeys" command = "hotkeys-help" category = "&Help" - is-checked = false - can-check = false - group = "" - is-disabled = false saved-params = "is-checked" window "mainwindow" elem "mainwindow" type = MAIN + pos = 0,0 size = 640x440 + anchor1 = none + anchor2 = none is-default = true - is-maximized = true + saved-params = "pos;size;is-minimized;is-maximized" title = "Paradise Station 13" + is-maximized = true icon = 'icons\\paradise.png' + macro = "default" menu = "menu" - macro = "macro" - saved-params = "pos;size;is-minimized;is-maximized" elem "asset_cache_browser" type = BROWSER - is-visible = false - elem "hotkey_toggle" - type = BUTTON - button-type = pushbox - pos = 560,420 - size = 80x20 - anchor1 = 100,100 + pos = 0,0 + size = 200x200 + anchor1 = none anchor2 = none - background-color = none + is-visible = false saved-params = "" - is-flat = false - text = "Hotkey Toggle" - command = ".winset \"mainwindow.macro != macro ? mainwindow.macro=macro hotkey_toggle.is-checked=false input.focus=true input.background-color=#d3b5b5 : mainwindow.macro=hotkeymode hotkey_toggle.is-checked=true mapwindow.map.focus=true input.background-color=#f0f0f0\"" elem "mainvsplit" type = CHILD pos = 3,0 size = 634x416 anchor1 = 0,0 anchor2 = 100,100 - is-vert = true - splitter = 50 - show-splitter = true + saved-params = "splitter" left = "mapwindow" right = "rpane" - saved-params = "splitter" + is-vert = true elem "input" type = INPUT pos = 3,420 size = 477x20 anchor1 = 0,100 anchor2 = 100,100 + background-color = #d3b5b5 is-default = true border = sunken - font-family = "" - font-size = 0 - font-style = "" - text-color = #000000 - background-color = #d3b5b5 saved-params = "command" elem "saybutton" type = BUTTON - button-type = pushbox - pos = 520,420 - size = 40x20 + pos = 480,420 + size = 80x20 anchor1 = 100,100 anchor2 = none - is-flat = false + saved-params = "is-checked" text = "Chat" command = ".winset \"saybutton.is-checked=true?input.command=\"!say \\\"\" macrobutton.is-checked=false:input.command=\"\"saybutton.is-checked=true?mebutton.is-checked=false\"" - is-checked = false - saved-params = "is-checked" + button-type = pushbox elem "mebutton" type = BUTTON - pos = 480,420 - size = 40x20 + pos = 560,420 + size = 80x20 anchor1 = 100,100 anchor2 = none saved-params = "is-checked" @@ -1594,54 +196,63 @@ window "mainwindow" type = BROWSER pos = 0,0 size = 999x999 + anchor1 = none + anchor2 = none is-visible = false + saved-params = "" window "mapwindow" elem "mapwindow" type = MAIN - is-pane = true - title = "Map window" + pos = 0,0 + size = 640x480 + anchor1 = none + anchor2 = none saved-params = "pos;size;is-minimized;is-maximized" + title = "Map window" + is-pane = true elem "map" type = MAP - is-default = true pos = 0,0 size = 640x480 anchor1 = 0,0 anchor2 = 100,100 font-family = "Arial" font-size = 7 - font-style = "" text-color = none - background-color = none - icon-size = 0 + is-default = true saved-params = "icon-size" window "outputwindow" elem "outputwindow" type = MAIN - is-pane = true - can-close = false - can-minimize = false + pos = 0,0 + size = 640x480 + anchor1 = none + anchor2 = none saved-params = "pos;size;is-minimized;is-maximized" title = "Output window" + can-close = false + can-minimize = false + is-pane = true elem "browseroutput" type = BROWSER - is-disabled = true - auto-format = false pos = 0,0 size = 640x480 anchor1 = 0,0 anchor2 = 100,100 - font-family = "" - font-size = 0 - font-style = "" - text-color = #000000 background-color = #ffffff + is-disabled = true + saved-params = "" + auto-format = false window "rpane" elem "rpane" type = MAIN + pos = 0,0 + size = 640x480 + anchor1 = none + anchor2 = none saved-params = "pos;size;is-minimized;is-maximized" is-pane = true elem "rpanewindow" @@ -1651,15 +262,14 @@ window "rpane" anchor1 = 0,0 anchor2 = 100,100 saved-params = "splitter" - left = "" right = "outputwindow" is-vert = false - splitter = 50 - show-splitter = true elem "textb" type = BUTTON pos = 0,7 size = 60x16 + anchor1 = none + anchor2 = none is-visible = false saved-params = "is-checked" text = "Text" @@ -1671,6 +281,8 @@ window "rpane" type = BUTTON pos = 64,7 size = 60x16 + anchor1 = none + anchor2 = none is-visible = false saved-params = "is-checked" text = "Info" @@ -1681,63 +293,89 @@ window "rpane" type = BUTTON pos = 155,7 size = 60x16 + anchor1 = none + anchor2 = none + saved-params = "is-checked" text = "Wiki" command = "wiki" elem "forumb" type = BUTTON pos = 220,7 size = 60x16 + anchor1 = none + anchor2 = none + saved-params = "is-checked" text = "Forum" command = "forum" elem "rulesb" type = BUTTON pos = 285,7 size = 60x16 + anchor1 = none + anchor2 = none + saved-params = "is-checked" text = "Rules" command = "rules" elem "githubb" type = BUTTON pos = 350,7 size = 60x16 + anchor1 = none + anchor2 = none + saved-params = "is-checked" text = "GitHub" command = "github" elem "changelog" type = BUTTON pos = 415,7 size = 67x16 + anchor1 = none + anchor2 = none + saved-params = "is-checked" text = "Changelog" command = "Changelog" elem "discordb" type = BUTTON pos = 487,7 size = 60x16 - background-color = #7289DA - text-color = #FFFFFF - font-style = bold + anchor1 = none + anchor2 = none + font-style = "bold" + text-color = #ffffff + background-color = #7289da + saved-params = "is-checked" text = "Discord" command = "discord" elem "karma" type = BUTTON pos = 552,7 size = 60x16 - background-color = #FF4500 - text-color = #FFFFFF - font-style = bold + anchor1 = none + anchor2 = none + font-style = "bold" + text-color = #ffffff + background-color = #ff4500 + saved-params = "is-checked" text = "Karma" command = "karmashop" elem "donate" type = BUTTON pos = 617,7 size = 60x16 + anchor1 = none + anchor2 = none + font-style = "bold" + text-color = #ffffff background-color = #008000 - text-color = #FFFFFF - font-style = bold + saved-params = "is-checked" text = "Donate" command = "Donate" elem "browseb" type = BUTTON pos = 561,7 size = 60x16 + anchor1 = none + anchor2 = none is-visible = false saved-params = "is-checked" text = "Browser" @@ -1748,9 +386,13 @@ window "rpane" window "browserwindow" elem "browserwindow" type = MAIN - is-pane = true + pos = 0,0 + size = 640x480 + anchor1 = none + anchor2 = none saved-params = "pos;size;is-minimized;is-maximized" title = "Browser" + is-pane = true elem "browser" type = BROWSER pos = 0,0 @@ -1758,15 +400,20 @@ window "browserwindow" anchor1 = 0,0 anchor2 = 100,100 is-default = true + saved-params = "" on-show = ".winset\"rpane.infob.is-visible=true?rpane.infob.pos=130,7;rpane.textb.is-visible=true;rpane.browseb.is-visible=true;rpane.browseb.is-checked=true;rpane.rpanewindow.pos=0,30;rpane.rpanewindow.size=0x0;rpane.rpanewindow.left=browserwindow\"" on-hide = ".winset\"rpane.infob.is-visible=true?rpane.infob.is-checked=true rpane.infob.pos=65,7 rpane.rpanewindow.left=infowindow:rpane.rpanewindow.left=textwindow rpane.textb.is-visible=true rpane.rpanewindow.pos=0,30 rpane.rpanewindow.size=0x0\"" window "infowindow" elem "infowindow" type = MAIN - is-pane = true + pos = 0,0 + size = 640x480 + anchor1 = none + anchor2 = none saved-params = "pos;size;is-minimized;is-maximized" title = "Info" + is-pane = true elem "info" type = INFO pos = 0,0 @@ -1774,9 +421,8 @@ window "infowindow" anchor1 = 0,0 anchor2 = 100,100 is-default = true + saved-params = "" highlight-color = #00aa00 - tab-text-color = #000000 - allow-html = true - multi-line = true on-show = ".winset\"rpane.infob.is-visible=true;rpane.browseb.is-visible=true?rpane.infob.pos=130,7:rpane.infob.pos=65,7 rpane.textb.is-visible=true rpane.infob.is-checked=true rpane.rpanewindow.pos=0,30 rpane.rpanewindow.size=0x0 rpane.rpanewindow.left=infowindow\"" on-hide = ".winset\"rpane.infob.is-visible=false;rpane.browseb.is-visible=true?rpane.browseb.is-checked=true rpane.rpanewindow.left=browserwindow:rpane.textb.is-visible=true rpane.rpanewindow.pos=0,30 rpane.rpanewindow.size=0x0 rpane.rpanewindow.left=\"" + diff --git a/paradise.dme b/paradise.dme index 8c846be4546c..aa90c6b58b77 100644 --- a/paradise.dme +++ b/paradise.dme @@ -24,6 +24,7 @@ #include "code\__DEFINES\bots.dm" #include "code\__DEFINES\callbacks.dm" #include "code\__DEFINES\clothing.dm" +#include "code\__DEFINES\colors.dm" #include "code\__DEFINES\combat.dm" #include "code\__DEFINES\components.dm" #include "code\__DEFINES\construction.dm" @@ -207,6 +208,7 @@ #include "code\controllers\subsystem\holiday.dm" #include "code\controllers\subsystem\icon_smooth.dm" #include "code\controllers\subsystem\idlenpcpool.dm" +#include "code\controllers\subsystem\input.dm" #include "code\controllers\subsystem\ipintel.dm" #include "code\controllers\subsystem\jobs.dm" #include "code\controllers\subsystem\lighting.dm" @@ -334,7 +336,6 @@ #include "code\datums\helper_datums\construction_datum.dm" #include "code\datums\helper_datums\events.dm" #include "code\datums\helper_datums\global_iterator.dm" -#include "code\datums\helper_datums\hotkey_modes.dm" #include "code\datums\helper_datums\icon_snapshot.dm" #include "code\datums\helper_datums\input.dm" #include "code\datums\helper_datums\map_template.dm" @@ -1588,6 +1589,16 @@ #include "code\modules\hydroponics\grown\tomato.dm" #include "code\modules\hydroponics\grown\towercap.dm" #include "code\modules\karma\karma.dm" +#include "code\modules\keybindings\bindings_admin.dm" +#include "code\modules\keybindings\bindings_atom.dm" +#include "code\modules\keybindings\bindings_carbon.dm" +#include "code\modules\keybindings\bindings_client.dm" +#include "code\modules\keybindings\bindings_human.dm" +#include "code\modules\keybindings\bindings_living.dm" +#include "code\modules\keybindings\bindings_mob.dm" +#include "code\modules\keybindings\bindings_robot.dm" +#include "code\modules\keybindings\focus.dm" +#include "code\modules\keybindings\setup.dm" #include "code\modules\library\admin.dm" #include "code\modules\library\codex_gigas.dm" #include "code\modules\library\lib_items.dm"