diff --git a/.travis.yml b/.travis.yml index 043755d6b9..566778c4c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ sudo: false env: BYOND_MAJOR="509" BYOND_MINOR="1318" - MACRO_COUNT=1004 + MACRO_COUNT=996 cache: directories: diff --git a/code/__defines/damage_organs.dm b/code/__defines/damage_organs.dm index 152e22a11a..124be53452 100644 --- a/code/__defines/damage_organs.dm +++ b/code/__defines/damage_organs.dm @@ -35,18 +35,20 @@ #define ORGAN_BLEEDING (1<<1) #define ORGAN_BROKEN (1<<2) #define ORGAN_DESTROYED (1<<3) -#define ORGAN_ROBOT (1<<4) -#define ORGAN_SPLINTED (1<<5) -#define ORGAN_DEAD (1<<6) -#define ORGAN_MUTATED (1<<7) -#define ORGAN_ASSISTED (1<<8) +#define ORGAN_SPLINTED (1<<4) +#define ORGAN_DEAD (1<<5) +#define ORGAN_MUTATED (1<<6) #define DROPLIMB_EDGE 0 #define DROPLIMB_BLUNT 1 #define DROPLIMB_BURN 2 // Damage above this value must be repaired with surgery. -#define ROBOLIMB_SELF_REPAIR_CAP 30 +#define ROBOLIMB_REPAIR_CAP 30 + +#define ORGAN_ASSISTED 1 // Like pacemakers, not robotic +#define ORGAN_ROBOT 2 // Fully robotic, no organic parts +#define ORGAN_LIFELIKE 3 // Robotic, made to appear organic //Germs and infections. #define GERM_LEVEL_AMBIENT 110 // Maximum germ level you can reach by standing still. diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index 3441f5ce2a..4a84b66083 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -38,24 +38,37 @@ #define LIFE_HUD 10 // STATUS_HUD that only reports dead or alive //some colors -#define COLOR_WHITE "#FFFFFF" -#define COLOR_SILVER "#C0C0C0" -#define COLOR_GRAY "#808080" -#define COLOR_BLACK "#000000" -#define COLOR_RED "#FF0000" -#define COLOR_MAROON "#800000" -#define COLOR_YELLOW "#FFFF00" -#define COLOR_OLIVE "#808000" -#define COLOR_LIME "#00FF00" -#define COLOR_GREEN "#008000" -#define COLOR_CYAN "#00FFFF" -#define COLOR_TEAL "#008080" -#define COLOR_BLUE "#0000FF" -#define COLOR_NAVY "#000080" -#define COLOR_PINK "#FF00FF" -#define COLOR_PURPLE "#800080" -#define COLOR_ORANGE "#FF9900" -#define COLOR_LUMINOL "#66FFFF" +#define COLOR_WHITE "#FFFFFF" +#define COLOR_SILVER "#C0C0C0" +#define COLOR_GRAY "#808080" +#define COLOR_BLACK "#000000" +#define COLOR_RED "#FF0000" +#define COLOR_MAROON "#800000" +#define COLOR_YELLOW "#FFFF00" +#define COLOR_OLIVE "#808000" +#define COLOR_LIME "#00FF00" +#define COLOR_GREEN "#008000" +#define COLOR_CYAN "#00FFFF" +#define COLOR_TEAL "#008080" +#define COLOR_BLUE "#0000FF" +#define COLOR_NAVY "#000080" +#define COLOR_PINK "#FF00FF" +#define COLOR_PURPLE "#800080" +#define COLOR_ORANGE "#FF9900" +#define COLOR_LUMINOL "#66FFFF" +#define COLOR_BEIGE "#CEB689" +#define COLOR_BLUE_GRAY "#6A97B0" +#define COLOR_BROWN "#B19664" +#define COLOR_DARK_BROWN "#917448" +#define COLOR_DARK_ORANGE "#B95A00" +#define COLOR_GREEN_GRAY "#8DAF6A" +#define COLOR_RED_GRAY "#AA5F61" +#define COLOR_PALE_BLUE_GRAY "#8BBBD5" +#define COLOR_PALE_GREEN_GRAY "#AED18B" +#define COLOR_PALE_RED_GRAY "#CC9090" +#define COLOR_PALE_PURPLE_GRAY "#BDA2BA" +#define COLOR_PURPLE_GRAY "#A2819E" + // Shuttles. // These define the time taken for the shuttle to get to the space station, and the time before it leaves again. @@ -143,19 +156,6 @@ #define PROJECTILE_CONTINUE -1 //if the projectile should continue flying after calling bullet_act() #define PROJECTILE_FORCE_MISS -2 //if the projectile should treat the attack as a miss (suppresses attack and admin logs) - only applies to mobs. -// Custom colors -#define COLOR_BEIGE "#CEB689" -#define COLOR_BLUE_GRAY "#6A97B0" -#define COLOR_BROWN "#B19664" -#define COLOR_DARK_BROWN "#917448" -#define COLOR_DARK_ORANGE "#B95A00" -#define COLOR_GREEN_GRAY "#8DAF6A" -#define COLOR_RED_GRAY "#AA5F61" -#define COLOR_PALE_BLUE_GRAY "#8BBBD5" -#define COLOR_PALE_GREEN_GRAY "#AED18B" -#define COLOR_PALE_RED_GRAY "#CC9090" -#define COLOR_PALE_PURPLE_GRAY "#BDA2BA" -#define COLOR_PURPLE_GRAY "#A2819E" // Vending stuff #define CAT_NORMAL 1 diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 8bac7f560c..68b313d9a1 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -13,7 +13,6 @@ #define GODMODE 0x1000 #define FAKEDEATH 0x2000 // Replaces stuff like changeling.changeling_fakedeath. #define DISFIGURED 0x4000 // Set but never checked. Remove this sometime and replace occurences with the appropriate organ code -#define XENO_HOST 0x8000 // Tracks whether we're gonna be a baby alien's mummy. // Grab levels. #define GRAB_PASSIVE 1 @@ -27,11 +26,11 @@ #define BORGXRAY 0x4 #define BORGMATERIAL 8 -#define HOSTILE_STANCE_IDLE 1 -#define HOSTILE_STANCE_ALERT 2 -#define HOSTILE_STANCE_ATTACK 3 -#define HOSTILE_STANCE_ATTACKING 4 -#define HOSTILE_STANCE_TIRED 5 +#define STANCE_IDLE 1 +#define STANCE_ALERT 2 +#define STANCE_ATTACK 3 +#define STANCE_ATTACKING 4 +#define STANCE_TIRED 5 #define LEFT 1 #define RIGHT 2 @@ -186,4 +185,9 @@ #define MOB_PULL_NONE 0 #define MOB_PULL_SMALLER 1 #define MOB_PULL_SAME 2 -#define MOB_PULL_LARGER 3 \ No newline at end of file +#define MOB_PULL_LARGER 3 + +//XENOBIO2 FLAGS +#define NOMUT 0 +#define COLORMUT 1 +#define SPECIESMUT 2 diff --git a/code/__defines/research.dm b/code/__defines/research.dm index d40722d0cf..9c3bec3066 100644 --- a/code/__defines/research.dm +++ b/code/__defines/research.dm @@ -12,7 +12,8 @@ #define TECH_ILLEGAL "syndicate" #define TECH_ARCANE "arcane" -#define IMPRINTER 0x1 //For circuits. Uses glass/chemicals. -#define PROTOLATHE 0x2 //New stuff. Uses glass/metal/chemicals -#define MECHFAB 0x4 //Mechfab -#define CHASSIS 0x8 //For protolathe, but differently +#define IMPRINTER 0x0001 //For circuits. Uses glass/chemicals. +#define PROTOLATHE 0x0002 //New stuff. Uses glass/metal/chemicals +#define MECHFAB 0x0004 //Mechfab +#define CHASSIS 0x0008 //For protolathe, but differently +#define PROSFAB 0x0010 //For prosthetics fab \ No newline at end of file diff --git a/code/_helpers/_global_objects.dm b/code/_helpers/_global_objects.dm new file mode 100644 index 0000000000..4cbbcf3314 --- /dev/null +++ b/code/_helpers/_global_objects.dm @@ -0,0 +1 @@ +var/datum/gear_tweak/color/gear_tweak_free_color_choice = new() \ No newline at end of file diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm index 06b277257f..57ff4b7013 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -248,6 +248,34 @@ . |= M // Since we're already looping through mobs, why bother using |= ? This only slows things down. return . +/proc/get_mobs_and_objs_in_view_fast(var/turf/T, var/range, var/checkghosts = 1) + + var/list/mobs = list() + var/list/objs = list() + + var/list/hear = dview(range,T,INVISIBILITY_MAXIMUM) + var/list/hearturfs = list() + + for(var/atom/movable/AM in hear) + if(ismob(AM)) + mobs += AM + hearturfs += AM.locs[1] + else if(isobj(AM)) + objs += AM + hearturfs += AM.locs[1] + + + for(var/mob/M in player_list) + if(checkghosts && M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) + mobs |= M + continue + if(M.loc && M.locs[1] in hearturfs) + mobs |= M + + return list("mobs" = mobs, "objs" = objs) + + + #define SIGN(X) ((X<0)?-1:1) proc diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm index f00e97d484..1a1eced63a 100644 --- a/code/_helpers/global_lists.dm +++ b/code/_helpers/global_lists.dm @@ -33,6 +33,8 @@ var/global/list/language_keys[0] // Table of say codes for all languages var/global/list/whitelisted_species = list("Human") // Species that require a whitelist check. var/global/list/playable_species = list("Human") // A list of ALL playable species, whitelisted, latejoin or otherwise. +var/list/mannequins_ + // Posters var/global/list/poster_designs = list() @@ -49,45 +51,7 @@ var/global/list/facial_hair_styles_male_list = list() var/global/list/facial_hair_styles_female_list = list() var/global/list/skin_styles_female_list = list() //unused //Underwear -var/global/list/underwear_top_t = list( - "Bra, Red" = "t1", "Bra, White" = "t2", "Bra, Yellow" = "t3", "Bra, Blue" = "t4", "Bra, Black" = "t5", "Lacy Bra" = "t6", "Sports Bra, Black" = "t7", "Sports Bra, White" = "t8", - "Sports Bra Alt, Black" = "t9", "Sporta Bra Alt, White" = "t10", "Bra, Baby-Blue" = "t11", "Bra, Green" = "t12", "Bra, Pink" = "t13", "Bra, Violet" = "t14", - "Lacy Bra Alt" = "t15", "Lacy Bra Alt, Violet" = "t16", "Halterneck Bra, Black" = "t17", "Halterneck Bra, Blue" = "t18", "Halterneck Bra, Green" = "t19", "Halterneck Bra, Purple" = "t20", - "Halterneck Bra, Red" = "t21", "Halterneck Bra, Teal" = "t22", "Halterneck Bra, Violet" = "t23", "Halterneck Bra, White" = "t24", "None") -var/global/list/underwear_bottom_t = list( - "Briefs, White" = "b1", "Briefs, Grey" = "b2", "Briefs, Green" = "b3", "Briefs, Blue" = "b4", "Briefs, Black" = "b5", "Boxers, Loveheart" = "b7", "Boxers, Black" = "b8", - "Boxers, Grey" = "b9", "Boxers, Green & Blue Striped" = "b10", "Panties, Red" = "b11", "Panties, White" = "b12", "Panties, Yellow" = "b13", "Panties, Blue" = "b14", - "Panties, Light-Black" = "b15", "Thong" = "b16", "Panties, Black" = "b17", "Panties Alt, White" = "b18", "Compression Shorts, Black" = "b19", "Compression Shorts, White" = "b20", - "Compression Shorts, Baby-Blue" = "b21", "Panties, Green" = "b22", "Compression Shorts, Pink" = "b23", "Thong, Violet" = "b24", "Thong Alt" = "b25", "Thong Alt, Violet" = "b26", - "Alt Thong, Black" = "b27", "Alt Thong, Blue" = "b28", "Alt Thong, Green" = "b29", "Alt Thong, Purple" = "b30", "Alt Thong, Red" = "b31", "Alt Thong, Teal" = "b32", - "Alt Thong, Violet" = "b33", "Alt Thong, White" = "b34", "None") - //undershirt -var/global/list/undershirt_t = list( - "White tank top" = "u1", "Black tank top" = "u2", "Black shirt" = "u3", - "White shirt" = "u4", "White shirt 2" = "shirt_white_s", "White tank top 2" = "tank_white_s", - "Black shirt 2" = "shirt_black_s", "Grey shirt" = "shirt_grey_s", "Heart shirt" = "lover_s", - "I love NT shirt" = "ilovent_s", "White shortsleeve shirt" = "whiteshortsleeve_s", "Purple shortsleeve shirt" = "purpleshortsleeve_s", - "Blue shortsleeve shirt" = "blueshortsleeve_s", "Green shortsleeve shirt" = "greenshortsleeve_s", "Black shortsleeve shirt" = "blackshortsleeve_s", - "Blue shirt" = "blueshirt_s", "Red shirt" = "redshirt_s", "Yellow shirt" = "yellowshirt_s", "Green shirt" = "greenshirt_s", - "Blue polo shirt" = "bluepolo_s", "Red polo shirt" = "redpolo_s", "White polo shirt" = "whitepolo_s", - "Grey-yellow polo shirt" = "grayyellowpolo_s", "Fire tank top" = "tank_fire_s", "NT shirt" = "shirt_nano_s", - "Blue shirt 2" = "shirt_blue_s", "Red shirt 2" = "shirt_red_s", "Red tank top" = "tank_red_s", "Green shirt 2" = "shirt_green_s", - "Tiedye shirt" = "shirt_tiedye_s", "Green sport shirt" = "greenshirtsport_s", "Red sport shirt" = "redshirtsport_s", - "Blue striped shirt" = "shirt_stripes_s", "Blue sport shirt" = "blueshirtsport_s", "None") - //Socks -var/global/list/socks_t = list( - "White normal" = "white_norm", "White short" = "white_short", "White knee" = "white_knee", - "White thigh" = "white_thigh", "Black normal" = "black_norm", "Black short" = "black_short", - "Black knee" = "black_knee", "Black thigh" = "black_thigh", "Thin knee" = "thin_knee", - "Thin thigh" = "thin_thigh", "Pantyhose" = "pantyhose", "Striped thigh" = "striped_thigh", - "Striped knee" = "striped_knee", "Rainbow knee" = "rainbow_knee", "Rainbow thigh" = "rainbow_thigh", - "Fishnets" = "fishnet", "Thin white thigh" = "thinwhite_thigh", "Thin white knee" = "thinwhite_knee", - "Green striped thigh" = "gstriped_thigh", "Green striped knee" = "gstriped_knee", - "Purple striped thigh" = "pstriped_thigh", "Purple striped knee" = "pstriped_knee", - "Blue striped thigh" = "bstriped_thigh", "Blue striped knee" = "bstriped_knee", - "Yellow striped thigh" = "ystriped_thigh", "Yellow striped knee" = "ystriped_knee", - "Red striped thigh" = "rstriped_thigh", "Red striped knee" = "rstriped_knee", - "Orange striped thigh" = "ostriped_thigh", "Orange striped knee" = "ostriped_knee", "None") +var/datum/category_collection/underwear/global_underwear = new() //Backpacks var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Alt") @@ -137,6 +101,14 @@ var/global/list/string_slot_flags = list( "holster" = SLOT_HOLSTER ) +/proc/get_mannequin(var/ckey) + if(!mannequins_) + mannequins_ = new() + . = mannequins_[ckey] + if(!.) + . = new/mob/living/carbon/human/dummy/mannequin() + mannequins_[ckey] = . + ////////////////////////// /////Initial Building///// ////////////////////////// @@ -231,3 +203,5 @@ var/global/list/string_slot_flags = list( . += " has: [t]\n" world << . */ +//Hexidecimal numbers +var/global/list/hexNums = list("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F") diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index b770693a69..0858ebc1a4 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -1039,10 +1039,9 @@ proc/get_mob_with_client_list() //gets the turf the atom is located in (or itself, if it is a turf). //returns null if the atom is not in a turf. -/proc/get_turf(atom/A) - if(!istype(A)) return - for(A, A && !isturf(A), A=A.loc); - return A +/proc/get_turf(atom/movable/A) + if(isturf(A)) return A + if(A && A.locs.len) return A.locs[1] /proc/get(atom/loc, type) while(loc) diff --git a/code/_macros.dm b/code/_macros.dm index e8caac8345..801498d915 100644 --- a/code/_macros.dm +++ b/code/_macros.dm @@ -36,3 +36,7 @@ #define issilicon(A) istype(A, /mob/living/silicon) #define isslime(A) istype(A, /mob/living/carbon/slime) + +#define isxeno(A) istype(A, /mob/living/simple_animal/xeno) + +#define RANDOM_BLOOD_TYPE pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+") diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm index c631a3435d..3f872f53e6 100644 --- a/code/_onclick/hud/human.dm +++ b/code/_onclick/hud/human.dm @@ -388,10 +388,7 @@ f_style = "Shaved" if(dna.species == "Human") //no more xenos losing ears/tentacles h_style = pick("Bedhead", "Bedhead 2", "Bedhead 3") - undershirt = null - underwear_top = null - underwear_bottom = null - socks = null + all_underwear.Cut() regenerate_icons() /obj/screen/ling diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 093975051b..3c65b03f73 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -1,40 +1,59 @@ +/* +=== Item Click Call Sequences === +These are the default click code call sequences used when clicking on stuff with an item. + +Atoms: + +mob/ClickOn() calls the item's resolve_attackby() proc. +item/resolve_attackby() calls the target atom's attackby() proc. + +Mobs: + +mob/living/attackby() after checking for surgery, calls the item's attack() proc. +item/attack() generates attack logs, sets click cooldown and calls the mob's attacked_with_item() proc. If you override this, consider whether you need to set a click cooldown, play attack animations, and generate logs yourself. +mob/attacked_with_item() should then do mob-type specific stuff (like determining hit/miss, handling shields, etc) and then possibly call the item's apply_hit_effect() proc to actually apply the effects of being hit. + +Item Hit Effects: + +item/apply_hit_effect() can be overriden to do whatever you want. However "standard" physical damage based weapons should make use of the target mob's hit_with_weapon() proc to +avoid code duplication. This includes items that may sometimes act as a standard weapon in addition to having other effects (e.g. stunbatons on harm intent). +*/ // Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown. /obj/item/proc/attack_self(mob/user) return +//I would prefer to rename this to attack(), but that would involve touching hundreds of files. +/obj/item/proc/resolve_attackby(atom/A, mob/user) + add_fingerprint(user) + return A.attackby(src, user) + // No comment /atom/proc/attackby(obj/item/W, mob/user) return /atom/movable/attackby(obj/item/W, mob/user) - if(!(W.flags&NOBLUDGEON)) + if(!(W.flags & NOBLUDGEON)) visible_message("[src] has been hit by [user] with [W].") /mob/living/attackby(obj/item/I, mob/user) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - if(istype(I) && ismob(user)) - I.attack(src, user) - + if(!ismob(user)) + return 0 + if(can_operate(src) && do_surgery(src,user,I)) //Surgery + return 1 + return I.attack(src, user, user.zone_sel.selecting) // Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person. // Click parameters is the params string from byond Click() code, see that documentation. /obj/item/proc/afterattack(atom/target, mob/user, proximity_flag, click_parameters) return -//TODO: refactor mob attack code. -/* -Busy writing something else that I don't want to get mixed up in a general attack code, and I don't want to forget this so leaving a note here. -leave attackby() as handling the general case of "using an item on a mob" -attackby() will decide to call attacked_by() or not. -attacked_by() will be made a living level proc and handle the specific case of "attacking with an item to cause harm" -attacked_by() will then call attack() so that stunbatons and other weapons that have special attack effects can do their thing. -attacked_by() will handle hitting/missing/logging as it does now, and will call attack() to apply the attack effects (damage) instead of the other way around (as it is now). -*/ - -/obj/item/proc/attack(mob/living/M as mob, mob/living/user as mob, def_zone) - - if(!istype(M) || (can_operate(M) && do_surgery(M,user,src))) return 0 +//I would prefer to rename this attack_as_weapon(), but that would involve touching hundreds of files. +/obj/item/proc/attack(mob/living/M, mob/living/user, var/target_zone) + if(!force || (flags & NOBLUDGEON)) + return 0 + if(M == user && user.a_intent != I_HURT) + return 0 ///////////////////////// user.lastattacked = M @@ -46,50 +65,22 @@ attacked_by() will handle hitting/missing/logging as it does now, and will call msg_admin_attack("[key_name(user)] attacked [key_name(M)] with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])" ) ///////////////////////// - // Attacking someone with a weapon while they are neck-grabbed - if(user.a_intent == I_HURT) - for(var/obj/item/weapon/grab/G in M.grabbed_by) - if(G.assailant == user && G.state >= GRAB_NECK) - M.attack_throat(src, G, user) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.do_attack_animation(M) + var/hit_zone = M.resolve_item_attack(src, user, target_zone) + if(hit_zone) + apply_hit_effect(M, user, hit_zone) + + return 1 + +//Called when a weapon is used to make a successful melee attack on a mob. Returns the blocked result +/obj/item/proc/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) + if(hitsound) + playsound(loc, hitsound, 50, 1, -1) + var/power = force if(HULK in user.mutations) power *= 2 + return target.hit_with_weapon(src, user, power, hit_zone) - // TODO: needs to be refactored into a mob/living level attacked_by() proc. ~Z - user.do_attack_animation(M) - user.break_cloak() - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - - // Handle striking to cripple. - var/dislocation_str - if(user.a_intent == I_DISARM) - dislocation_str = H.attack_joint(src, user, def_zone) - if(H.attacked_by(src, user, def_zone) && hitsound) - playsound(loc, hitsound, 50, 1, -1) - spawn(1) //ugh I hate this but I don't want to root through human attack procs to print it after this call resolves. - if(dislocation_str) user.visible_message("[dislocation_str]") - return 1 - return 0 - else - if(attack_verb.len) - user.visible_message("[M] has been [pick(attack_verb)] with [src] by [user]!") - else - user.visible_message("[M] has been attacked with [src] by [user]!") - - if (hitsound) - playsound(loc, hitsound, 50, 1, -1) - switch(damtype) - if("brute") - M.take_organ_damage(power) - if(prob(33)) // Added blood for whacking non-humans too - var/turf/simulated/location = get_turf(M) - if(istype(location)) location.add_blood_floor(M) - if("fire") - if (!(COLD_RESISTANCE in M.mutations)) - M.take_organ_damage(0, power) - M << "Aargh it burns!" - M.updatehealth() - add_fingerprint(user) - return 1 diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index bb7bf300d0..ee361c9888 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -25,7 +25,7 @@ usr.client.debug_variables(antag) message_admins("Admin [key_name_admin(usr)] is debugging the [antag.role_text] template.") -/client/proc/debug_controller(controller in list("Master","Ticker","Ticker Process","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras", "Transfer Controller", "Gas Data","Event","Plants","Alarm","Nano","Chemistry","Vote")) +/client/proc/debug_controller(controller in list("Master","Ticker","Ticker Process","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras", "Transfer Controller", "Gas Data","Event","Plants","Alarm","Nano","Chemistry","Vote","Xenobio")) set category = "Debug" set name = "Debug Controller" set desc = "Debug the various periodic loop controllers for the game (be careful!)" @@ -95,5 +95,8 @@ if("Vote") debug_variables(vote) feedback_add_details("admin_verb", "DVote") + if("Xenobio") + debug_variables(xenobio_controller) + feedback_add_details("admin_verb", "DXenobio") message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.") return diff --git a/code/datums/browser.dm b/code/datums/browser.dm index f5549fa1a8..577698532e 100644 --- a/code/datums/browser.dm +++ b/code/datums/browser.dm @@ -28,6 +28,9 @@ height = nheight if (nref) ref = nref + // If a client exists, but they have disabled fancy windowing, disable it! + if(user && user.client && !user.client.is_preference_enabled(/datum/client_preference/browser_style)) + return add_stylesheet("common", 'html/browser/common.css') // this CSS sheet is common to all UIs /datum/browser/proc/set_title(ntitle) @@ -178,4 +181,4 @@ if(src && src.mob) //world << "[src] was [src.mob.machine], setting to null" src.mob.unset_machine() - return \ No newline at end of file + return diff --git a/code/datums/category.dm b/code/datums/category.dm index 01cbc21386..3bc9640b79 100644 --- a/code/datums/category.dm +++ b/code/datums/category.dm @@ -2,17 +2,20 @@ * Category Collection * **********************/ /datum/category_collection - var/category_group_type // The type of categories to initialize - var/list/datum/category_group/categories // The list of initialized categories + var/category_group_type // Type of categories to initialize + var/list/datum/category_group/categories // List of initialized categories + var/list/datum/category_group/categories_by_name // Associative list of initialized categories, keyed by name /datum/category_collection/New() ..() categories = new() + categories_by_name = new() for(var/category_type in typesof(category_group_type)) var/datum/category_group/category = category_type if(initial(category.name)) category = new category(src) categories += category + categories_by_name[category.name] = category categories = dd_sortedObjectList(categories) /datum/category_collection/Destroy() @@ -26,20 +29,23 @@ ******************/ /datum/category_group var/name = "" - var/category_item_type // The type of items to initialize - var/list/datum/category_item/items // The list of initialized items - var/datum/category_collection/collection // The collection this group belongs to + var/category_item_type // Type of items to initialize + var/list/datum/category_item/items // List of initialized items + var/list/datum/category_item/items_by_name // Associative list of initialized items, by name + var/datum/category_collection/collection // The collection this group belongs to /datum/category_group/New(var/datum/category_collection/cc) ..() collection = cc items = new() + items_by_name = new() for(var/item_type in typesof(category_item_type)) var/datum/category_item/item = item_type if(initial(item.name)) item = new item(src) items += item + items_by_name[item.name] = item // For whatever reason dd_insertObjectList(items, item) doesn't insert in the correct order // If you change this, confirm that character setup doesn't become completely unordered. diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm index 9d2f3e6f45..11c1fa87fc 100644 --- a/code/datums/supplypacks.dm +++ b/code/datums/supplypacks.dm @@ -618,6 +618,35 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee access = access_robotics group = "Engineering" +/datum/supply_packs/robolimbs_basic + name = "Basic robolimb blueprints" + contains = list( + /obj/item/weapon/disk/limb/morpheus, + /obj/item/weapon/disk/limb/xion + ) + cost = 15 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Robolimb blueprints (basic)" + access = access_robotics + group = "Engineering" + +/datum/supply_packs/robolimbs_adv + name = "All robolimb blueprints" + contains = list( + /obj/item/weapon/disk/limb/bishop, + /obj/item/weapon/disk/limb/hesphiastos, + /obj/item/weapon/disk/limb/morpheus, + /obj/item/weapon/disk/limb/veymed, + /obj/item/weapon/disk/limb/wardtakahashi, + /obj/item/weapon/disk/limb/xion, + /obj/item/weapon/disk/limb/zenghu, + ) + cost = 40 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Robolimb blueprints (adv)" + access = access_robotics + group = "Engineering" + /datum/supply_packs/phoron name = "Phoron assembly crate" contains = list( @@ -1242,16 +1271,19 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee /datum/supply_packs/carpet name = "Imported carpet" - containertype = /obj/structure/closet + containertype = /obj/structure/closet/crate containername = "Imported carpet crate" cost = 15 group = "Miscellaneous" - contains = list(/obj/item/stack/tile/carpet) + contains = list( + /obj/item/stack/tile/carpet, + /obj/item/stack/tile/carpet/blue + ) amount = 50 /datum/supply_packs/linoleum name = "Linoleum" - containertype = /obj/structure/closet + containertype = /obj/structure/closet/crate containername = "Linoleum crate" cost = 15 group = "Miscellaneous" diff --git a/code/datums/underwear/bottom.dm b/code/datums/underwear/bottom.dm new file mode 100644 index 0000000000..7f91a18608 --- /dev/null +++ b/code/datums/underwear/bottom.dm @@ -0,0 +1,56 @@ +/datum/category_item/underwear/bottom/none + name = "None" + always_last = TRUE + +/datum/category_item/underwear/bottom/briefs + name = "Briefs" + icon_state = "briefs" + has_color = TRUE + +/datum/category_item/underwear/bottom/briefs/is_default(var/gender) + return gender != FEMALE + +/datum/category_item/underwear/bottom/boxers_loveheart + name = "Boxers, Loveheart" + icon_state = "boxers_loveheart" + +/datum/category_item/underwear/bottom/boxers + name = "Boxers" + icon_state = "boxers" + has_color = TRUE + +/datum/category_item/underwear/bottom/boxers_green_and_blue + name = "Boxers, green & blue striped" + icon_state = "boxers_green_and_blue" + +/datum/category_item/underwear/bottom/panties + name = "Panties" + icon_state = "panties" + has_color = TRUE + +/datum/category_item/underwear/bottom/panties/is_default(var/gender) + return gender == FEMALE + +/datum/category_item/underwear/bottom/lacy_thong + name = "Lacy thong" + icon_state = "lacy_thong" + +/datum/category_item/underwear/bottom/lacy_thong_alt + name = "Lacy thong, alt" + icon_state = "lacy_thong_alt" + has_color = TRUE + +/datum/category_item/underwear/bottom/panties_alt + name = "Panties, alt" + icon_state = "panties_alt" + has_color = TRUE + +/datum/category_item/underwear/bottom/compression_shorts + name = "Compression shorts" + icon_state = "compression_shorts" + has_color = TRUE + +/datum/category_item/underwear/bottom/thong + name = "Thong" + icon_state = "thong" + has_color = TRUE \ No newline at end of file diff --git a/code/datums/underwear/socks.dm b/code/datums/underwear/socks.dm new file mode 100644 index 0000000000..fc1c96ce53 --- /dev/null +++ b/code/datums/underwear/socks.dm @@ -0,0 +1,59 @@ +/datum/category_item/underwear/socks/none + always_last = TRUE + name = "None" + +/datum/category_item/underwear/socks/normal + name = "Normal" + icon_state = "socks_norm" + has_color = TRUE + +/datum/category_item/underwear/socks/short + name = "Short" + icon_state = "socks_short" + has_color = TRUE + +/datum/category_item/underwear/socks/thigh + name = "Thigh" + icon_state = "socks_thigh" + has_color = TRUE + +/datum/category_item/underwear/socks/knee + name = "Knee" + icon_state = "socks_knee" + has_color = TRUE + +/datum/category_item/underwear/socks/striped_knee + name = "Knee, striped" + icon_state = "striped_knee" + has_color = TRUE + +/datum/category_item/underwear/socks/striped_thigh + name = "Thigh, striped" + icon_state = "striped_thigh" + has_color = TRUE + +/datum/category_item/underwear/socks/pantyhose + name = "Pantyhose" + icon_state = "pantyhose" + +/datum/category_item/underwear/socks/thin_thigh + name = "Thigh, thin" + icon_state = "thin_thigh" + has_color = TRUE + +/datum/category_item/underwear/socks/thin_knee + name = "Knee, thin" + icon_state = "thin_knee" + has_color = TRUE + +/datum/category_item/underwear/socks/rainbow_thigh + name = "Thigh, rainbow" + icon_state = "rainbow_thigh" + +/datum/category_item/underwear/socks/rainbow_knee + name = "Knee, rainbow" + icon_state = "rainbow_knee" + +/datum/category_item/underwear/socks/fishnet + name = "Fishnet" + icon_state = "fishnet" \ No newline at end of file diff --git a/code/datums/underwear/top.dm b/code/datums/underwear/top.dm new file mode 100644 index 0000000000..8640e77da9 --- /dev/null +++ b/code/datums/underwear/top.dm @@ -0,0 +1,43 @@ +/datum/category_item/underwear/top/none + name = "None" + always_last = TRUE + +/datum/category_item/underwear/top/none/is_default(var/gender) + return gender != FEMALE + +/datum/category_item/underwear/top/bra + is_default = TRUE + name = "Bra" + icon_state = "bra" + has_color = TRUE + +/datum/category_item/underwear/top/bra/is_default(var/gender) + return gender == FEMALE + +/datum/category_item/underwear/top/sports_bra + name = "Sports bra" + icon_state = "sports_bra" + has_color = TRUE + +/datum/category_item/underwear/top/sports_bra_alt + name = "Sports bra, alt" + icon_state = "sports_bra_alt" + has_color = TRUE + +/datum/category_item/underwear/top/lacy_bra + name = "Lacy bra" + icon_state = "lacy_bra" + +/datum/category_item/underwear/top/lacy_bra_alt + name = "Lacy bra, alt" + icon_state = "lacy_bra_alt" + has_color = TRUE + +/datum/category_item/underwear/top/lacy_bra_alt_stripe + name = "Lacy bra, alt, stripe" + icon_state = "lacy_bra_alt_stripe" + +/datum/category_item/underwear/top/halterneck_bra + name = "Halterneck bra" + icon_state = "halterneck_bra" + has_color = TRUE \ No newline at end of file diff --git a/code/datums/underwear/undershirts.dm b/code/datums/underwear/undershirts.dm new file mode 100644 index 0000000000..33886f997d --- /dev/null +++ b/code/datums/underwear/undershirts.dm @@ -0,0 +1,65 @@ +/datum/category_item/underwear/undershirt/none + is_default = TRUE + name = "None" + always_last = TRUE + +/datum/category_item/underwear/undershirt/shirt + name = "Shirt" + icon_state = "undershirt" + has_color = TRUE + +/datum/category_item/underwear/undershirt/tank_top + name = "Tank top" + icon_state = "tanktop" + has_color = TRUE + +/datum/category_item/underwear/undershirt/tank_top_alt + name = "Tank top, alt" + icon_state = "tanktop_alt" + has_color = TRUE + +/datum/category_item/underwear/undershirt/tank_top_fire + name = "Tank top, fire" + icon_state = "tank_fire_s" + +/datum/category_item/underwear/undershirt/shirt_heart + name = "Shirt, heart" + icon_state = "lover_s" + +/datum/category_item/underwear/undershirt/shirt_nt + name = "Shirt, NT" + icon_state = "shirt_nano_s" + +/datum/category_item/underwear/undershirt/shirt_love_nt + name = "Shirt, I<3NT" + icon_state = "lover_s" + +/datum/category_item/underwear/undershirt/shortsleeve_shirt + name = "Shortsleeve shirt" + icon_state = "shortsleeve" + has_color = TRUE + +/datum/category_item/underwear/undershirt/polo_shirt + name = "Polo shirt" + icon_state = "polo" + has_color = TRUE + +/datum/category_item/underwear/undershirt/sport_shirt_green + name = "Sport shirt, green" + icon_state = "greenshirtsport_s" + +/datum/category_item/underwear/undershirt/sport_shirt_red + name = "Sport shirt, red" + icon_state = "redshirtsport_s" + +/datum/category_item/underwear/undershirt/sport_shirt_blue + name = "Sport shirt, blue" + icon_state = "blueshirtsport_s" + +/datum/category_item/underwear/undershirt/shirt_tiedye + name = "Shirt, tiedye" + icon_state = "shirt_tiedye_s" + +/datum/category_item/underwear/undershirt/shirt_blue_striped + name = "Shirt, blue stripes" + icon_state = "shirt_stripes_s" \ No newline at end of file diff --git a/code/datums/underwear/underwear.dm b/code/datums/underwear/underwear.dm new file mode 100644 index 0000000000..d14ddea133 --- /dev/null +++ b/code/datums/underwear/underwear.dm @@ -0,0 +1,66 @@ +/**************************** +* Category Collection Setup * +****************************/ +/datum/category_collection/underwear + category_group_type = /datum/category_group/underwear + +/************* +* Categories * +*************/ +/datum/category_group/underwear + var/sort_order // Lower sort order is applied as icons first + +datum/category_group/underwear/dd_SortValue() + return sort_order + +/datum/category_group/underwear/top + name = "Underwear, top" + sort_order = 1 + category_item_type = /datum/category_item/underwear/top + +/datum/category_group/underwear/bottom + name = "Underwear, bottom" + sort_order = 2 + category_item_type = /datum/category_item/underwear/bottom + +/datum/category_group/underwear/socks + name = "Socks" + sort_order = 3 + category_item_type = /datum/category_item/underwear/socks + +/datum/category_group/underwear/undershirt + name = "Undershirt" + sort_order = 4 // Undershirts currently have the highest sort order because they may cover both underwear and socks. + category_item_type = /datum/category_item/underwear/undershirt + +/******************* +* Category entries * +*******************/ +/datum/category_item/underwear + var/always_last = FALSE // Should this entry be sorte last? + var/is_default = FALSE // Should this entry be considered the default for its type? + var/icon = 'icons/mob/human.dmi' // Which icon to get the underwear from + var/icon_state // And the particular item state + var/list/tweaks = list() // Underwear customizations. + var/has_color = FALSE + +/datum/category_item/underwear/New() + if(has_color) + tweaks += gear_tweak_free_color_choice + +/datum/category_item/underwear/dd_SortValue() + if(always_last) + return "~"+name + return name + +/datum/category_item/underwear/proc/is_default(var/gender) + return is_default + +/datum/category_item/underwear/proc/generate_image(var/list/metadata) + if(!icon_state) + return + + var/image/I = image(icon = 'icons/mob/human.dmi', icon_state = icon_state) + for(var/datum/gear_tweak/gt in tweaks) + gt.tweak_item(I, metadata && metadata["[gt]"] ? metadata["[gt]"] : gt.get_default()) + return I \ No newline at end of file diff --git a/code/game/antagonist/alien/borer.dm b/code/game/antagonist/alien/borer.dm index 783055381c..7483fb508b 100644 --- a/code/game/antagonist/alien/borer.dm +++ b/code/game/antagonist/alien/borer.dm @@ -46,7 +46,7 @@ var/datum/antagonist/borer/borers for(var/mob/living/carbon/human/H in mob_list) if(H.stat != DEAD && !H.has_brain_worms()) var/obj/item/organ/external/head = H.get_organ(BP_HEAD) - if(head && !(head.status & ORGAN_ROBOT)) + if(head && !(head.robotic >= ORGAN_ROBOT)) host = H break if(istype(host)) diff --git a/code/game/antagonist/outsider/deathsquad.dm b/code/game/antagonist/outsider/deathsquad.dm index 3a61152ab2..1234bdb1f6 100644 --- a/code/game/antagonist/outsider/deathsquad.dm +++ b/code/game/antagonist/outsider/deathsquad.dm @@ -70,7 +70,7 @@ var/datum/antagonist/deathsquad/deathsquad var/syndicate_commando_name = pick(last_names) var/datum/preferences/A = new() //Randomize appearance for the commando. - A.randomize_appearance_for(player.current) + A.randomize_appearance_and_body_for(player.current) player.name = "[syndicate_commando_rank] [syndicate_commando_name]" player.current.name = player.name diff --git a/code/game/antagonist/outsider/raider.dm b/code/game/antagonist/outsider/raider.dm index 0a68becfd6..08a6e8644b 100644 --- a/code/game/antagonist/outsider/raider.dm +++ b/code/game/antagonist/outsider/raider.dm @@ -207,7 +207,7 @@ var/datum/antagonist/raider/raiders player.equip_to_slot_or_del(new new_shoes(player),slot_shoes) if(!player.shoes) //If equipping shoes failed, fall back to equipping sandals - var/fallback_type = pick(/obj/item/clothing/shoes/sandal, /obj/item/clothing/shoes/jackboots/unathi) + var/fallback_type = pick(/obj/item/clothing/shoes/sandal, /obj/item/clothing/shoes/jackboots/toeless) player.equip_to_slot_or_del(new fallback_type(player), slot_shoes) player.equip_to_slot_or_del(new new_uniform(player),slot_w_uniform) diff --git a/code/game/gamemodes/changeling/powers/bioelectrogenesis.dm b/code/game/gamemodes/changeling/powers/bioelectrogenesis.dm index dd1489248f..6dd22efab9 100644 --- a/code/game/gamemodes/changeling/powers/bioelectrogenesis.dm +++ b/code/game/gamemodes/changeling/powers/bioelectrogenesis.dm @@ -6,10 +6,10 @@ Shocking someone costs ten chemicals per use." enhancedtext = "Shocking biologicals without grabbing only requires five chemicals, and has more disabling power." genomecost = 2 - verbpath = /mob/proc/changeling_bioelectrogenesis + verbpath = /mob/living/carbon/human/proc/changeling_bioelectrogenesis //Recharge whatever's in our hand, or shock people. -/mob/proc/changeling_bioelectrogenesis() +/mob/living/carbon/human/proc/changeling_bioelectrogenesis() set category = "Changeling" set name = "Bioelectrogenesis (20 + 10/shock)" set desc = "Recharges anything in your hand, or shocks people." @@ -33,19 +33,28 @@ return 0 else + // Handle glove conductivity. + var/obj/item/clothing/gloves/gloves = src.gloves + var/siemens = 1 + if(gloves) + siemens = gloves.siemens_coefficient + //If we're grabbing someone, electrocute them. if(istype(held_item,/obj/item/weapon/grab)) var/obj/item/weapon/grab/G = held_item if(G.affecting) - G.affecting.electrocute_act(10,src,1.0,BP_TORSO) - var/agony = 80 //Does more than if hit with an electric hand, since grabbing is slower. + G.affecting.electrocute_act(10 * siemens,src,1.0,BP_TORSO) + var/agony = 80 * siemens //Does more than if hit with an electric hand, since grabbing is slower. G.affecting.stun_effect_act(0, agony, BP_TORSO, src) msg_admin_attack("[key_name(src)] shocked [key_name(G.affecting)] with the [src].") - visible_message("Arcs of electricity strike [G.affecting]!", - "Our hand channels raw electricity into [G.affecting].", - "You hear sparks!") + if(siemens) + visible_message("Arcs of electricity strike [G.affecting]!", + "Our hand channels raw electricity into [G.affecting].", + "You hear sparks!") + else + src << "Our gloves block us from shocking \the [G.affecting]." src.mind.changeling.chem_charges -= 10 return 1 @@ -69,17 +78,19 @@ "Our hand channels raw electricity into \the [held_item].", "You hear sparks!") var/i = 10 - while(i) - cell.charge += 100 //This should be a nice compromise between recharging guns and other batteries. - if(cell.charge > cell.maxcharge) - cell.charge = cell.maxcharge - break - var/T = get_turf(src) - new /obj/effect/effect/sparks(T) - held_item.update_icon() - i-- - sleep(1 SECOND) - success = 1 + if(siemens) + while(i) + cell.charge += 100 * siemens //This should be a nice compromise between recharging guns and other batteries. + if(cell.charge > cell.maxcharge) + cell.charge = cell.maxcharge + break + if(siemens) + var/T = get_turf(src) + new /obj/effect/effect/sparks(T) + held_item.update_icon() + i-- + sleep(1 SECOND) + success = 1 if(success == 0) //If we couldn't do anything with the ability, don't deduct the chemicals. src << "We are unable to affect \the [held_item]." else @@ -113,11 +124,18 @@ if(src) qdel(src) -/obj/item/weapon/electric_hand/afterattack(var/atom/target, var/mob/living/user, proximity) +/obj/item/weapon/electric_hand/afterattack(var/atom/target, var/mob/living/carbon/human/user, proximity) if(!target) return if(!proximity) return + + // Handle glove conductivity. + var/obj/item/clothing/gloves/gloves = user.gloves + var/siemens = 1 + if(gloves) + siemens = gloves.siemens_coefficient + //Excuse the copypasta. if(istype(target,/mob/living/carbon)) var/mob/living/carbon/C = target @@ -126,14 +144,17 @@ src << "We require more chemicals to electrocute [C]!" return 0 - C.electrocute_act(electrocute_amount,src,1.0,BP_TORSO) - C.stun_effect_act(0, agony_amount, BP_TORSO, src) + C.electrocute_act(electrocute_amount * siemens,src,1.0,BP_TORSO) + C.stun_effect_act(0, agony_amount * siemens, BP_TORSO, src) msg_admin_attack("[key_name(user)] shocked [key_name(C)] with the [src].") - visible_message("Arcs of electricity strike [C]!", - "Our hand channels raw electricity into [C]", - "You hear sparks!") + if(siemens) + visible_message("Arcs of electricity strike [C]!", + "Our hand channels raw electricity into [C]", + "You hear sparks!") + else + src << "Our gloves block us from shocking \the [C]." //qdel(src) //Since we're no longer a one hit stun, we need to stick around. user.mind.changeling.chem_charges -= shock_cost return 1 @@ -145,11 +166,12 @@ src << "We require more chemicals to electrocute [S]!" return 0 - S.electrocute_act(60,src,1.0) //If only they had surge protectors. - visible_message("Arcs of electricity strike [S]!", - "Our hand channels raw electricity into [S]", - "You hear sparks!") - S << "Warning: Electrical surge detected!" + S.electrocute_act(60 * siemens,src,1.0) //If only they had surge protectors. + if(siemens) + visible_message("Arcs of electricity strike [S]!", + "Our hand channels raw electricity into [S]", + "You hear sparks!") + S << "Warning: Electrical surge detected!" //qdel(src) user.mind.changeling.chem_charges -= 10 return 1 @@ -164,20 +186,22 @@ "Our hand channels raw electricity into \the [target].", "You hear sparks!") var/i = 10 - while(i) - cell.charge += 100 //This should be a nice compromise between recharging guns and other batteries. - if(cell.charge > cell.maxcharge) - cell.charge = cell.maxcharge - break //No point making sparks if the cell's full. -// if(!Adjacent(T)) -// break - var/Turf = get_turf(src) - new /obj/effect/effect/sparks(Turf) - T.update_icon() - i-- - sleep(1 SECOND) - success = 1 - break + if(siemens) + while(i) + cell.charge += 100 * siemens //This should be a nice compromise between recharging guns and other batteries. + if(cell.charge > cell.maxcharge) + cell.charge = cell.maxcharge + break //No point making sparks if the cell's full. + // if(!Adjacent(T)) + // break + if(siemens) + var/Turf = get_turf(src) + new /obj/effect/effect/sparks(Turf) + T.update_icon() + i-- + sleep(1 SECOND) + success = 1 + break if(success == 0) src << "We are unable to affect \the [target]." else diff --git a/code/game/gamemodes/changeling/powers/fake_death.dm b/code/game/gamemodes/changeling/powers/fake_death.dm index 0018eabad6..ac7080301b 100644 --- a/code/game/gamemodes/changeling/powers/fake_death.dm +++ b/code/game/gamemodes/changeling/powers/fake_death.dm @@ -18,14 +18,13 @@ var/mob/living/carbon/C = src if(changeling.max_geneticpoints < 0) //Absorbed by another ling - src << "You have no genomes, not even your own, and cannot regenerate." + src << "We have no genomes, not even our own, and cannot regenerate." return 0 - if(!C.stat && alert("Are we sure we wish to fake our death?",,"Yes","No") == "No")//Confirmation for living changelings if they want to fake their death + if(!C.stat && alert("Are we sure we wish to regenerate? We will appear to be dead while doing so.","Revival","Yes","No") == "No") return C << "We will attempt to regenerate our form." - C.status_flags |= FAKEDEATH //play dead C.update_canmove() C.remove_changeling_powers() @@ -33,13 +32,12 @@ C.suiciding = 0 if(C.stat != DEAD) - C.emote("deathgasp") - C.tod = worldtime2text() + C.adjustOxyLoss(C.maxHealth * 2) spawn(rand(800,2000)) //The ling will now be able to choose when to revive src.verbs += /mob/proc/changeling_revive - src << "We are ready to rise. Use the Revive verb when you are ready." + src << "We are ready to rise. Use the Revive verb when you are ready." feedback_add_details("changeling_powers","FD") return 1 \ No newline at end of file diff --git a/code/game/gamemodes/changeling/powers/revive.dm b/code/game/gamemodes/changeling/powers/revive.dm index 4f53c95b46..79fd2dcf74 100644 --- a/code/game/gamemodes/changeling/powers/revive.dm +++ b/code/game/gamemodes/changeling/powers/revive.dm @@ -25,8 +25,6 @@ C.SetStunned(0) C.SetWeakened(0) C.radiation = 0 - C.halloss = 0 - C.shock_stage = 0 //Pain C.heal_overall_damage(C.getBruteLoss(), C.getFireLoss()) C.reagents.clear_reagents() C.restore_all_organs(ignore_prosthetic_prefs=1) //Covers things like fractures and other things not covered by the above. @@ -36,8 +34,13 @@ H.mutations.Remove(HUSK) H.status_flags -= DISFIGURED H.update_body(1) + for(var/limb in H.organs_by_name) + var/obj/item/organ/external/current_limb = H.organs_by_name[limb] + current_limb.undislocate() + + C.halloss = 0 + C.shock_stage = 0 //Pain C << "We have regenerated." - C.status_flags &= ~FAKEDEATH C.update_canmove() C.mind.changeling.purchased_powers -= C feedback_add_details("changeling_powers","CR") diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index 564fc4d004..1a0aa9e1c5 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -6,23 +6,35 @@ w_class = 4 force = 30 throwforce = 10 + hitsound = 'sound/weapons/bladeslice.ogg' attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") /obj/item/weapon/melee/cultblade/cultify() return -/obj/item/weapon/melee/cultblade/attack(mob/living/target as mob, mob/living/carbon/human/user as mob) +/obj/item/weapon/melee/cultblade/attack(mob/living/M, mob/living/user, var/target_zone) if(iscultist(user)) - playsound(loc, 'sound/weapons/bladeslice.ogg', 50, 1, -1) return ..() + + var/zone = (user.hand ? "l_arm":"r_arm") + if(ishuman(user)) + var/mob/living/carbon/human/H = user + var/obj/item/organ/external/affecting = H.get_organ(zone) + user << "An unexplicable force rips through your [affecting.name], tearing the sword from your grasp!" else - user.Paralyse(5) - user << "An unexplicable force powerfully repels the sword from [target]!" - var/organ = ((user.hand ? "l_":"r_") + "arm") - var/obj/item/organ/external/affecting = user.get_organ(organ) - if(affecting.take_damage(rand(force/2, force))) //random amount of damage between half of the blade's force and the full force of the blade. - user.UpdateDamageIcon() - return + user << "An unexplicable force rips through you, tearing the sword from your grasp!" + + //random amount of damage between half of the blade's force and the full force of the blade. + user.apply_damage(rand(force/2, force), BRUTE, zone, 0, sharp=1, edge=1) + user.Weaken(5) + + user.drop_from_inventory(src, src.loc) + throw_at(get_edge_target_turf(src, pick(alldirs)), rand(1,3), throw_speed) + + var/spooky = pick('sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg', 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/wail.ogg') + playsound(loc, spooky, 50, 1) + + return 1 /obj/item/weapon/melee/cultblade/pickup(mob/living/user as mob) if(!iscultist(user)) diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm index 37445326ca..3cb41cc872 100644 --- a/code/game/gamemodes/cult/runes.dm +++ b/code/game/gamemodes/cult/runes.dm @@ -470,8 +470,7 @@ var/list/sacrificed = list() D.r_eyes = 200 D.g_eyes = 200 D.update_eyes() - D.underwear_top = 0 - D.underwear_bottom = 0 + D.all_underwear.Cut() D.key = ghost.key cult.add_antagonist(D.mind) diff --git a/code/game/jobs/job/assistant.dm b/code/game/jobs/job/assistant.dm index ff87fb58e4..5e2b40d952 100644 --- a/code/game/jobs/job/assistant.dm +++ b/code/game/jobs/job/assistant.dm @@ -13,21 +13,16 @@ minimal_access = list() //See /datum/job/assistant/get_access() alt_titles = list("Technical Assistant","Medical Intern","Research Assistant","Visitor", "Resident") -/datum/job/assistant/equip(var/mob/living/carbon/human/H) +/datum/job/assistant/equip(var/mob/living/carbon/human/H, var/alt_title) if(!H) return 0 switch(H.backbag) if(2) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack(H), slot_back) if(3) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel/norm(H), slot_back) if(4) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel(H), slot_back) - if (H.mind.role_alt_title) - switch(H.mind.role_alt_title) - if("Visitor") //I doubt someone visiting the station would want to wear an ugly grey uniform - H.equip_to_slot_or_del(new /obj/item/clothing/under/assistantformal(H), slot_w_uniform) - - if("Resident") - H.equip_to_slot_or_del(new /obj/item/clothing/under/color/white(H), slot_w_uniform) - else - H.equip_to_slot_or_del(new /obj/item/clothing/under/color/grey(H), slot_w_uniform) + if(has_alt_title(H, alt_title,"Visitor")) //I doubt someone visiting the station would want to wear an ugly grey uniform + H.equip_to_slot_or_del(new /obj/item/clothing/under/assistantformal(H), slot_w_uniform) + else if(has_alt_title(H, alt_title,"Resident")) + H.equip_to_slot_or_del(new /obj/item/clothing/under/color/white(H), slot_w_uniform) else H.equip_to_slot_or_del(new /obj/item/clothing/under/color/grey(H), slot_w_uniform) H.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(H), slot_shoes) diff --git a/code/game/jobs/job/captain.dm b/code/game/jobs/job/captain.dm index 015441f32f..69f837d8f9 100644 --- a/code/game/jobs/job/captain.dm +++ b/code/game/jobs/job/captain.dm @@ -30,10 +30,17 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1) if(2) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/captain(H), slot_back) if(3) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel/cap(H), slot_back) if(4) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel(H), slot_back) - var/obj/item/clothing/under/U = new /obj/item/clothing/under/rank/captain(H) + H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/captain(H), slot_w_uniform) if(H.age>49) - U.accessories += new /obj/item/clothing/accessory/medal/gold/captain(U) - H.equip_to_slot_or_del(U, slot_w_uniform) + // Since we can have something other than the default uniform at this + // point, check if we can actually attach the medal + var/obj/item/clothing/uniform = H.w_uniform + var/obj/item/clothing/accessory/medal/gold/captain/medal = new() + + if(uniform && uniform.can_attach_accessory(medal)) + uniform.attach_accessory(null, medal) + else + qdel(medal) H.equip_to_slot_or_del(new /obj/item/device/pda/captain(H), slot_belt) H.equip_to_slot_or_del(new /obj/item/clothing/shoes/brown(H), slot_shoes) H.equip_to_slot_or_del(new /obj/item/clothing/head/caphat(H), slot_head) diff --git a/code/game/jobs/job/civilian_chaplain.dm b/code/game/jobs/job/civilian_chaplain.dm index cf162c1494..61cba70023 100644 --- a/code/game/jobs/job/civilian_chaplain.dm +++ b/code/game/jobs/job/civilian_chaplain.dm @@ -14,7 +14,7 @@ alt_titles = list("Counselor") - equip(var/mob/living/carbon/human/H) + equip(var/mob/living/carbon/human/H, var/alt_title, var/ask_questions = TRUE) if(!H) return 0 var/obj/item/weapon/storage/bible/B = new /obj/item/weapon/storage/bible(H) //BS12 EDIT @@ -22,6 +22,10 @@ H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/chaplain(H), slot_w_uniform) H.equip_to_slot_or_del(new /obj/item/device/pda/chaplain(H), slot_belt) H.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(H), slot_shoes) + if(!ask_questions) + return 1 + + spawn(0) var/religion_name = "Christianity" var/new_religion = sanitize(input(H, "You are the crew services officer. Would you like to change your religion? Default is Christianity, in SPACE.", "Name change", religion_name), MAX_NAME_LEN) @@ -150,3 +154,6 @@ feedback_set_details("religion_deity","[new_deity]") feedback_set_details("religion_book","[new_book_style]") return 1 + +/datum/job/chaplain/equip_preview(var/mob/living/carbon/human/H, var/alt_title) + return equip(H, alt_title, FALSE) diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm index 2ae944adfd..6bdf3c1c4a 100644 --- a/code/game/jobs/job/job.dm +++ b/code/game/jobs/job/job.dm @@ -71,8 +71,8 @@ H << "Your account number is: [M.account_number], your account pin is: [M.remote_access_pin]" // overrideable separately so AIs/borgs can have cardborg hats without unneccessary new()/del() -/datum/job/proc/equip_preview(mob/living/carbon/human/H) - return equip(H) +/datum/job/proc/equip_preview(mob/living/carbon/human/H, var/alt_title) + . = equip(H, alt_title) /datum/job/proc/get_access() if(!config || config.jobs_have_minimal_access) @@ -104,3 +104,6 @@ /datum/job/proc/is_position_available() return (current_positions < total_positions) || (total_positions == -1) + +/datum/job/proc/has_alt_title(var/mob/H, var/supplied_title, var/desired_title) + return (supplied_title == desired_title) || (H.mind && H.mind.role_alt_title == desired_title) \ No newline at end of file diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm index 8a769df0b5..eeb0c8072a 100644 --- a/code/game/jobs/job/medical.dm +++ b/code/game/jobs/job/medical.dm @@ -23,7 +23,7 @@ minimal_player_age = 10 ideal_character_age = 50 - equip(var/mob/living/carbon/human/H) + equip(var/mob/living/carbon/human/H, var/alt_title) if(!H) return 0 H.equip_to_slot_or_del(new /obj/item/device/radio/headset/heads/cmo(H), slot_l_ear) switch(H.backbag) @@ -53,7 +53,7 @@ minimal_access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_virology, access_eva) alt_titles = list("Surgeon","Emergency Physician","Nurse","Virologist") - equip(var/mob/living/carbon/human/H) + equip(var/mob/living/carbon/human/H, var/alt_title) if(!H) return 0 H.equip_to_slot_or_del(new /obj/item/device/radio/headset/headset_med(H), slot_l_ear) H.equip_to_slot_or_del(new /obj/item/clothing/shoes/white(H), slot_shoes) @@ -62,35 +62,33 @@ if(2) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/medic(H), slot_back) if(3) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel/med(H), slot_back) if(4) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel(H), slot_back) - if (H.mind.role_alt_title) - switch(H.mind.role_alt_title) - if("Emergency Physician") - H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/fr_jacket(H), slot_wear_suit) - if("Surgeon") - H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical/blue(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/labcoat(H), slot_wear_suit) - H.equip_to_slot_or_del(new /obj/item/clothing/head/surgery/blue(H), slot_head) - if("Virologist") - H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/virologist(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/labcoat/virologist(H), slot_wear_suit) - H.equip_to_slot_or_del(new /obj/item/clothing/mask/surgical(H), slot_wear_mask) - switch(H.backbag) - if(2) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/virology(H), slot_back) - if(3) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel/vir(H), slot_back) - if(4) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel(H), slot_back) - if("Medical Doctor") - H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/labcoat(H), slot_wear_suit) - if("Nurse") - if(H.gender == FEMALE) - if(prob(50)) - H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/nursesuit(H), slot_w_uniform) - else - H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/nurse(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/head/nursehat(H), slot_head) - else - H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical/purple(H), slot_w_uniform) + if(has_alt_title(H, alt_title,"Emergency Physician")) + H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/fr_jacket(H), slot_wear_suit) + else if(has_alt_title(H, alt_title,"Surgeon")) + H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical/blue(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/labcoat(H), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/clothing/head/surgery/blue(H), slot_head) + else if(has_alt_title(H, alt_title,"Virologist")) + H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/virologist(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/labcoat/virologist(H), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/clothing/mask/surgical(H), slot_wear_mask) + switch(H.backbag) + if(2) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/virology(H), slot_back) + if(3) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel/vir(H), slot_back) + if(4) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel(H), slot_back) + else if(has_alt_title(H, alt_title,"Medical Doctor")) + H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/labcoat(H), slot_wear_suit) + else if(has_alt_title(H, alt_title,"Nurse")) + if(H.gender == FEMALE) + if(prob(50)) + H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/nursesuit(H), slot_w_uniform) + else + H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/nurse(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/head/nursehat(H), slot_head) + else + H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical/purple(H), slot_w_uniform) else H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical(H), slot_w_uniform) H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/labcoat(H), slot_wear_suit) @@ -176,19 +174,17 @@ minimal_access = list(access_medical, access_medical_equip, access_psychiatrist) alt_titles = list("Psychologist") - equip(var/mob/living/carbon/human/H) + equip(var/mob/living/carbon/human/H, var/alt_title) if(!H) return 0 H.equip_to_slot_or_del(new /obj/item/device/radio/headset/headset_med(H), slot_l_ear) switch(H.backbag) if(2) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack(H), slot_back) if(3) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel/norm(H), slot_back) if(4) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel(H), slot_back) - if (H.mind.role_alt_title) - switch(H.mind.role_alt_title) - if("Psychiatrist") - H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/psych(H), slot_w_uniform) - if("Psychologist") - H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/psych/turtleneck(H), slot_w_uniform) + if(has_alt_title(H, alt_title,"Psychiatrist")) + H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/psych(H), slot_w_uniform) + else if(has_alt_title(H, alt_title,"Psychologist")) + H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/psych/turtleneck(H), slot_w_uniform) else H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical(H), slot_w_uniform) H.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(H), slot_shoes) @@ -211,7 +207,7 @@ minimal_access = list(access_medical, access_medical_equip, access_morgue, access_eva, access_maint_tunnels, access_external_airlocks) alt_titles = list("Emergency Medical Technician") - equip(var/mob/living/carbon/human/H) + equip(var/mob/living/carbon/human/H, var/alt_title) if(!H) return 0 H.equip_to_slot_or_del(new /obj/item/device/radio/headset/headset_med(H), slot_l_ear) H.equip_to_slot_or_del(new /obj/item/clothing/shoes/jackboots(H), slot_shoes) @@ -220,14 +216,12 @@ if(2) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/medic(H), slot_back) if(3) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel/med(H), slot_back) if(4) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel(H), slot_back) - if (H.mind.role_alt_title) - switch(H.mind.role_alt_title) - if("Emergency Medical Technician") - H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical/paramedic(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/fr_jacket(H), slot_wear_suit) - if("Paramedic") - H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical/black(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/fr_jacket(H), slot_wear_suit) + if(has_alt_title(H, alt_title,"Emergency Medical Technician")) + H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical/paramedic(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/fr_jacket(H), slot_wear_suit) + else if(has_alt_title(H, alt_title,"Paramedic")) + H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical/black(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/fr_jacket(H), slot_wear_suit) else H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/medical(H), slot_w_uniform) H.equip_to_slot_or_del(new /obj/item/weapon/storage/belt/medical/emt(H), slot_belt) diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm index dfb3659e0d..6320a475ce 100644 --- a/code/game/jobs/job/security.dm +++ b/code/game/jobs/job/security.dm @@ -98,7 +98,7 @@ minimal_access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_eva, access_external_airlocks) economic_modifier = 5 minimal_player_age = 3 - equip(var/mob/living/carbon/human/H) + equip(var/mob/living/carbon/human/H, var/alt_title) if(!H) return 0 H.equip_to_slot_or_del(new /obj/item/device/radio/headset/headset_sec(H), slot_l_ear) switch(H.backbag) @@ -114,7 +114,7 @@ H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/evidence(H), slot_l_hand) else H.equip_to_slot_or_del(new /obj/item/weapon/storage/box/evidence(H), slot_in_backpack) - if(H.mind.role_alt_title && H.mind.role_alt_title == "Forensic Technician") + if(has_alt_title(H, alt_title,"Forensic Technician")) H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/forensics/blue(H), slot_wear_suit) H.equip_to_slot_or_del(new /obj/item/weapon/storage/briefcase/crimekit, slot_r_hand) else diff --git a/code/game/jobs/job/silicon.dm b/code/game/jobs/job/silicon.dm index fb9aeb793f..d9aa96f710 100644 --- a/code/game/jobs/job/silicon.dm +++ b/code/game/jobs/job/silicon.dm @@ -29,6 +29,7 @@ /datum/job/ai/equip_preview(mob/living/carbon/human/H) H.equip_to_slot_or_del(new /obj/item/clothing/suit/straight_jacket(H), slot_wear_suit) H.equip_to_slot_or_del(new /obj/item/clothing/head/cardborg(H), slot_head) + return 1 /datum/job/cyborg title = "Cyborg" @@ -60,3 +61,4 @@ /datum/job/cyborg/equip_preview(mob/living/carbon/human/H) H.equip_to_slot_or_del(new /obj/item/clothing/suit/cardborg(H), slot_wear_suit) H.equip_to_slot_or_del(new /obj/item/clothing/head/cardborg(H), slot_head) + return 1 diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index 90ff3f0d6b..cd811a2862 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -433,8 +433,9 @@ bled = "Bleeding:" if(e.status & ORGAN_BROKEN) AN = "[e.broken_description]:" - if(e.status & ORGAN_ROBOT) - robot = "Prosthetic:" + switch(e.robotic) + if(ORGAN_ROBOT) robot = "Prosthetic:" + if(ORGAN_ASSISTED) robot = "Augmented:" if(e.open) open = "Open:" @@ -478,7 +479,7 @@ var/mech = "" if(i.status & ORGAN_ASSISTED) mech = "Assisted:" - if(i.status & ORGAN_ROBOT) + if(i.robotic >= ORGAN_ROBOT) mech = "Mechanical:" var/infection = "None" diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 83b6f2ce86..bdab3bbbb1 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -360,7 +360,7 @@ return if(subject.isSynthetic()) - scantemp = "Error: Subject is not organic." + scantemp = "Error: Majority of subject is non-organic." return if (subject.suiciding == 1) scantemp = "Error: Subject's brain is not responding to scanning stimuli." diff --git a/code/game/machinery/computer/guestpass.dm b/code/game/machinery/computer/guestpass.dm index a50680ef8d..a4553403bc 100644 --- a/code/game/machinery/computer/guestpass.dm +++ b/code/game/machinery/computer/guestpass.dm @@ -148,9 +148,9 @@ if(reas) reason = reas if ("duration") - var/dur = input("Duration (in minutes) during which pass is valid (up to 30 minutes).", "Duration") as num|null + var/dur = input("Duration (in minutes) during which pass is valid (up to 120 minutes).", "Duration") as num|null if (dur) - if (dur > 0 && dur <= 30) + if (dur > 0 && dur <= 120) duration = dur else usr << "Invalid duration." @@ -158,7 +158,7 @@ var/A = text2num(href_list["access"]) if (A in accesses) accesses.Remove(A) - else + else if(A in giver.access) //Let's make sure the ID card actually has the access. accesses.Add(A) else diff --git a/code/game/machinery/frame.dm b/code/game/machinery/frame.dm index a264df51f2..1dddcedea0 100644 --- a/code/game/machinery/frame.dm +++ b/code/game/machinery/frame.dm @@ -218,7 +218,10 @@ if(component_check) playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) var/obj/machinery/new_machine = new src.circuit.build_path(src.loc, src.dir) + // Handle machines that have allocated default parts in thier constructor. if(new_machine.component_parts) + for(var/CP in new_machine.component_parts) + qdel(CP) new_machine.component_parts.Cut() else new_machine.component_parts = list() diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm index 59d8f2bd08..3cc537be49 100644 --- a/code/game/machinery/jukebox.dm +++ b/code/game/machinery/jukebox.dm @@ -19,6 +19,7 @@ datum/track/New(var/title_name, var/audio) use_power = 1 idle_power_usage = 10 active_power_usage = 100 + circuit = /obj/item/weapon/circuitboard/jukebox var/playing = 0 @@ -35,6 +36,14 @@ datum/track/New(var/title_name, var/audio) new/datum/track("Trai`Tor", 'sound/music/traitor.ogg'), ) +/obj/machinery/media/jukebox/New() + ..() + circuit = new circuit(src) + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/capacitor(src) + component_parts += new /obj/item/weapon/stock_parts/console_screen(src) + component_parts += new /obj/item/stack/cable_coil(src, 5) + RefreshParts() /obj/machinery/media/jukebox/Destroy() StopPlaying() @@ -163,6 +172,10 @@ datum/track/New(var/title_name, var/audio) /obj/machinery/media/jukebox/attackby(obj/item/W as obj, mob/user as mob) src.add_fingerprint(user) + if(default_deconstruction_screwdriver(user, W)) + return + if(default_deconstruction_crowbar(user, W)) + return if(istype(W, /obj/item/weapon/wrench)) if(playing) StopPlaying() diff --git a/code/game/machinery/kitchen/smartfridge.dm b/code/game/machinery/kitchen/smartfridge.dm index ac13a52bc4..05de6b9ac6 100644 --- a/code/game/machinery/kitchen/smartfridge.dm +++ b/code/game/machinery/kitchen/smartfridge.dm @@ -57,15 +57,23 @@ return 0 /obj/machinery/smartfridge/secure/extract - name = "\improper Slime Extract Storage" - desc = "A refrigerated storage unit for slime extracts" + name = "\improper Biological Sample Storage" + desc = "A refrigerated storage unit for xenobiological samples." req_access = list(access_research) /obj/machinery/smartfridge/secure/extract/accept_check(var/obj/item/O as obj) - if(istype(O,/obj/item/slime_extract)) + if(istype(O,/obj/item/xenoproduct/)) return 1 return 0 +/obj/machinery/smartfridge/secure/extract/New() + ..() + for(var/i=1 to 5) + var/obj/item/xenoproduct/slime/core/C = new(src) + C.traits = new() + C.nameVar = "grey" + item_quants[C.name]++ + /obj/machinery/smartfridge/secure/medbay name = "\improper Refrigerated Medicine Storage" desc = "A refrigerated storage unit for storing medicine and chemicals." diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index 77aed2300f..71d63afc0a 100644 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -7,7 +7,7 @@ obj/machinery/recharger anchored = 1 use_power = 1 idle_power_usage = 4 - active_power_usage = 15000 //15 kW + active_power_usage = 40000 //40 kW var/obj/item/charging = null var/list/allowed_devices = list(/obj/item/weapon/gun/energy, /obj/item/weapon/melee/baton, /obj/item/device/laptop, /obj/item/weapon/cell) var/icon_state_charged = "recharger2" diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index e0047a08bf..36c8c62814 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -6,9 +6,9 @@ name = "space heater" desc = "Made by Space Amish using traditional space techniques, this heater is guaranteed not to set the station on fire." var/obj/item/weapon/cell/cell - var/cell_type = /obj/item/weapon/cell/apc + var/cell_type = /obj/item/weapon/cell/high var/on = 0 - var/set_temperature = T0C + 50 //K + var/set_temperature = T0C + 20 //K var/heating_power = 40000 diff --git a/code/game/machinery/telecomms/machine_interactions.dm b/code/game/machinery/telecomms/machine_interactions.dm index 2b3561395f..002da7bc53 100644 --- a/code/game/machinery/telecomms/machine_interactions.dm +++ b/code/game/machinery/telecomms/machine_interactions.dm @@ -196,7 +196,7 @@ // Returns a multitool from a user depending on their mobtype. -/obj/machinery/telecomms/proc/get_multitool(mob/user as mob) +/obj/machinery/proc/get_multitool(mob/user as mob) //No need to have this being a telecomms specific proc. var/obj/item/device/multitool/P = null // Let's double check diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 912bacb817..f086a3e1dd 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -907,7 +907,7 @@ /obj/item/seeds/sunflowerseed = 3,/obj/item/seeds/tomatoseed = 3,/obj/item/seeds/towermycelium = 3,/obj/item/seeds/wheatseed = 3,/obj/item/seeds/appleseed = 3, /obj/item/seeds/poppyseed = 3,/obj/item/seeds/sugarcaneseed = 3,/obj/item/seeds/ambrosiavulgarisseed = 3,/obj/item/seeds/peanutseed = 3,/obj/item/seeds/whitebeetseed = 3,/obj/item/seeds/watermelonseed = 3,/obj/item/seeds/limeseed = 3, /obj/item/seeds/lemonseed = 3,/obj/item/seeds/orangeseed = 3,/obj/item/seeds/grassseed = 3,/obj/item/seeds/cocoapodseed = 3,/obj/item/seeds/plumpmycelium = 2, - /obj/item/seeds/cabbageseed = 3,/obj/item/seeds/grapeseed = 3,/obj/item/seeds/pumpkinseed = 3,/obj/item/seeds/cherryseed = 3,/obj/item/seeds/plastiseed = 3,/obj/item/seeds/riceseed = 3) + /obj/item/seeds/cabbageseed = 3,/obj/item/seeds/grapeseed = 3,/obj/item/seeds/pumpkinseed = 3,/obj/item/seeds/cherryseed = 3,/obj/item/seeds/plastiseed = 3,/obj/item/seeds/riceseed = 3,/obj/item/seeds/lavenderseed = 3) contraband = list(/obj/item/seeds/amanitamycelium = 2,/obj/item/seeds/glowshroom = 2,/obj/item/seeds/libertymycelium = 2,/obj/item/seeds/mtearseed = 2, /obj/item/seeds/nettleseed = 2,/obj/item/seeds/reishimycelium = 2,/obj/item/seeds/reishimycelium = 2,/obj/item/seeds/shandseed = 2,) premium = list(/obj/item/toy/waterflower = 1) @@ -1047,3 +1047,24 @@ /obj/item/weapon/screwdriver = 5,/obj/item/weapon/crowbar = 5) //everything after the power cell had no amounts, I improvised. -Sayu +/obj/machinery/vending/fitness + name = "SweatMAX" + desc = "Fueled by your inner inadequacy!" + icon_state = "fitness" + products = list(/obj/item/weapon/reagent_containers/food/drinks/milk/smallcarton = 8, + /obj/item/weapon/reagent_containers/food/drinks/milk/smallcarton/chocolate = 8, + /obj/item/weapon/reagent_containers/food/drinks/drinkingglass/fitnessflask/proteinshake = 8, + /obj/item/weapon/reagent_containers/food/drinks/drinkingglass/fitnessflask = 8, + /obj/item/weapon/reagent_containers/food/snacks/candy/proteinbar = 8, + /obj/item/weapon/reagent_containers/food/snacks/liquidfood = 8, + /obj/item/weapon/reagent_containers/pill/diet = 8) + + prices = list(/obj/item/weapon/reagent_containers/food/drinks/milk/smallcarton = 3, + /obj/item/weapon/reagent_containers/food/drinks/milk/smallcarton/chocolate = 3, + /obj/item/weapon/reagent_containers/food/drinks/drinkingglass/fitnessflask/proteinshake = 20, + /obj/item/weapon/reagent_containers/food/drinks/drinkingglass/fitnessflask = 5, + /obj/item/weapon/reagent_containers/food/snacks/candy/proteinbar = 5, + /obj/item/weapon/reagent_containers/food/snacks/liquidfood = 5, + /obj/item/weapon/reagent_containers/pill/diet = 25) + + contraband = list(/obj/item/weapon/reagent_containers/syringe/steroid = 4) diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm index 425b959d31..ee000d60be 100644 --- a/code/game/mecha/mech_fabricator.dm +++ b/code/game/mecha/mech_fabricator.dm @@ -2,7 +2,7 @@ icon = 'icons/obj/robotics.dmi' icon_state = "fab-idle" name = "Exosuit Fabricator" - desc = "A machine used for construction of robotcs and mechas." + desc = "A machine used for construction of mechas." density = 1 anchored = 1 use_power = 1 @@ -23,12 +23,11 @@ var/list/categories = list() var/category = null - var/manufacturer = null var/sync_message = "" /obj/machinery/mecha_part_fabricator/New() ..() - + circuit = new circuit(src) component_parts = list() component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) @@ -41,7 +40,6 @@ return /obj/machinery/mecha_part_fabricator/initialize() - manufacturer = basic_robolimb.company update_categories() /obj/machinery/mecha_part_fabricator/process() @@ -99,13 +97,6 @@ data["buildable"] = get_build_options() data["category"] = category data["categories"] = categories - if(all_robolimbs) - var/list/T = list() - for(var/A in all_robolimbs) - var/datum/robolimb/R = all_robolimbs[A] - T += list(list("id" = A, "company" = R.company)) - data["manufacturers"] = T - data["manufacturer"] = manufacturer data["materials"] = get_materials() data["maxres"] = res_max_amount data["sync"] = sync_message @@ -133,10 +124,6 @@ if(href_list["category"] in categories) category = href_list["category"] - if(href_list["manufacturer"]) - if(href_list["manufacturer"] in all_robolimbs) - manufacturer = href_list["manufacturer"] - if(href_list["eject"]) eject_materials(href_list["eject"], text2num(href_list["amount"])) @@ -158,43 +145,32 @@ if(default_part_replacement(user, I)) return - var/material - switch(I.type) - if(/obj/item/stack/material/gold) - material = "gold" - if(/obj/item/stack/material/silver) - material = "silver" - if(/obj/item/stack/material/diamond) - material = "diamond" - if(/obj/item/stack/material/phoron) - material = "phoron" - if(/obj/item/stack/material/steel) - material = DEFAULT_WALL_MATERIAL - if(/obj/item/stack/material/glass) - material = "glass" - if(/obj/item/stack/material/uranium) - material = "uranium" + if(istype(I,/obj/item/stack/material)) + var/obj/item/stack/material/S = I + if(!(S.material.name in materials)) + user << "The [src] doesn't accept [S.material]!" + return + + var/sname = "[S.name]" + var/amnt = S.perunit + if(materials[S.material.name] + amnt <= res_max_amount) + if(S && S.amount >= 1) + var/count = 0 + overlays += "fab-load-metal" + spawn(10) + overlays -= "fab-load-metal" + while(materials[S.material.name] + amnt <= res_max_amount && S.amount >= 1) + materials[S.material.name] += amnt + S.use(1) + count++ + user << "You insert [count] [sname] into the fabricator." + update_busy() else - return ..() + user << "The fabricator cannot hold more [sname]." - var/obj/item/stack/material/stack = I - var/sname = "[stack.name]" - var/amnt = stack.perunit + return - if(materials[material] + amnt <= res_max_amount) - if(stack && stack.amount >= 1) - var/count = 0 - overlays += "fab-load-metal" - spawn(10) - overlays -= "fab-load-metal" - while(materials[material] + amnt <= res_max_amount && stack.amount >= 1) - materials[material] += amnt - stack.use(1) - count++ - user << "You insert [count] [sname] into the fabricator." - update_busy() - else - user << "The fabricator cannot hold more [sname]." + ..() /obj/machinery/mecha_part_fabricator/emag_act(var/remaining_charges, var/mob/user) switch(emagged) @@ -254,7 +230,7 @@ for(var/M in D.materials) materials[M] = max(0, materials[M] - D.materials[M] * mat_efficiency) if(D.build_path) - var/obj/new_item = D.Fabricate(loc, src) + var/obj/new_item = D.Fabricate(get_step(get_turf(src), src.dir), src) visible_message("\The [src] pings, indicating that \the [D] is complete.", "You hear a ping.") if(mat_efficiency != 1) if(new_item.matter && new_item.matter.len > 0) @@ -301,36 +277,20 @@ /obj/machinery/mecha_part_fabricator/proc/eject_materials(var/material, var/amount) // 0 amount = 0 means ejecting a full stack; -1 means eject everything var/recursive = amount == -1 ? 1 : 0 - material = lowertext(material) - var/mattype - switch(material) - if(DEFAULT_WALL_MATERIAL) - mattype = /obj/item/stack/material/steel - if("glass") - mattype = /obj/item/stack/material/glass - if("gold") - mattype = /obj/item/stack/material/gold - if("silver") - mattype = /obj/item/stack/material/silver - if("diamond") - mattype = /obj/item/stack/material/diamond - if("phoron") - mattype = /obj/item/stack/material/phoron - if("uranium") - mattype = /obj/item/stack/material/uranium - else - return - var/obj/item/stack/material/S = new mattype(loc) + var/matstring = lowertext(material) + var/material/M = get_material_by_name(matstring) + + var/obj/item/stack/material/S = M.place_sheet(get_turf(src)) if(amount <= 0) amount = S.max_amount - var/ejected = min(round(materials[material] / S.perunit), amount) + var/ejected = min(round(materials[matstring] / S.perunit), amount) S.amount = min(ejected, amount) if(S.amount <= 0) qdel(S) return - materials[material] -= ejected * S.perunit - if(recursive && materials[material] >= S.perunit) - eject_materials(material, -1) + materials[matstring] -= ejected * S.perunit + if(recursive && materials[matstring] >= S.perunit) + eject_materials(matstring, -1) update_busy() /obj/machinery/mecha_part_fabricator/proc/sync() diff --git a/code/game/mecha/mech_prosthetics.dm b/code/game/mecha/mech_prosthetics.dm new file mode 100644 index 0000000000..561a5bb3d8 --- /dev/null +++ b/code/game/mecha/mech_prosthetics.dm @@ -0,0 +1,333 @@ +/obj/machinery/pros_fabricator + icon = 'icons/obj/robotics.dmi' + icon_state = "pros-idle" + name = "Prosthetics Fabricator" + desc = "A machine used for construction of prosthetics." + density = 1 + anchored = 1 + use_power = 1 + idle_power_usage = 20 + active_power_usage = 5000 + req_access = list(access_robotics) + circuit = /obj/item/weapon/circuitboard/prosthetics + + var/speed = 1 + var/mat_efficiency = 1 + var/list/materials = list(DEFAULT_WALL_MATERIAL = 0, "glass" = 0, "gold" = 0, "silver" = 0, "diamond" = 0, "phoron" = 0, "uranium" = 0, "plasteel" = 0) + var/res_max_amount = 200000 + + var/datum/research/files + var/list/datum/design/queue = list() + var/progress = 0 + var/busy = 0 + + var/list/categories = list() + var/category = null + var/manufacturer = null + var/sync_message = "" + +/obj/machinery/pros_fabricator/New() + ..() + circuit = new circuit(src) + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/micro_laser(src) + component_parts += new /obj/item/weapon/stock_parts/console_screen(src) + RefreshParts() + + files = new /datum/research(src) //Setup the research data holder. + return + +/obj/machinery/pros_fabricator/initialize() + manufacturer = basic_robolimb.company + update_categories() + +/obj/machinery/pros_fabricator/process() + ..() + if(stat) + return + if(busy) + use_power = 2 + progress += speed + check_build() + else + use_power = 1 + update_icon() + +/obj/machinery/pros_fabricator/update_icon() + overlays.Cut() + if(panel_open) + icon_state = "pros-o" + else + icon_state = "pros-idle" + if(busy) + overlays += "pros-active" + +/obj/machinery/pros_fabricator/dismantle() + for(var/f in materials) + eject_materials(f, -1) + ..() + +/obj/machinery/pros_fabricator/RefreshParts() + res_max_amount = 0 + for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts) + res_max_amount += M.rating * 100000 // 200k -> 600k + var/T = 0 + for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts) + T += M.rating + mat_efficiency = 1 - (T - 1) / 4 // 1 -> 0.5 + for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts) // Not resetting T is intended; speed is affected by both + T += M.rating + speed = T / 2 // 1 -> 3 + +/obj/machinery/pros_fabricator/attack_hand(var/mob/user) + if(..()) + return + if(!allowed(user)) + return + ui_interact(user) + +/obj/machinery/pros_fabricator/ui_interact(var/mob/user, var/ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + var/data[0] + + var/datum/design/current = queue.len ? queue[1] : null + if(current) + data["current"] = current.name + data["queue"] = get_queue_names() + data["buildable"] = get_build_options() + data["category"] = category + data["categories"] = categories + if(all_robolimbs) + var/list/T = list() + for(var/A in all_robolimbs) + var/datum/robolimb/R = all_robolimbs[A] + if(R.unavailable_to_build) continue + T += list(list("id" = A, "company" = R.company)) + data["manufacturers"] = T + data["manufacturer"] = manufacturer + data["materials"] = get_materials() + data["maxres"] = res_max_amount + data["sync"] = sync_message + if(current) + data["builtperc"] = round((progress / current.time) * 100) + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if(!ui) + ui = new(user, src, ui_key, "mechfab.tmpl", "Prosthetics Fab UI", 800, 600) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/pros_fabricator/Topic(href, href_list) + if(..()) + return + + if(href_list["build"]) + add_to_queue(text2num(href_list["build"])) + + if(href_list["remove"]) + remove_from_queue(text2num(href_list["remove"])) + + if(href_list["category"]) + if(href_list["category"] in categories) + category = href_list["category"] + + if(href_list["manufacturer"]) + if(href_list["manufacturer"] in all_robolimbs) + manufacturer = href_list["manufacturer"] + + if(href_list["eject"]) + eject_materials(href_list["eject"], text2num(href_list["amount"])) + + if(href_list["sync"]) + sync() + else + sync_message = "" + + return 1 + +/obj/machinery/pros_fabricator/attackby(var/obj/item/I, var/mob/user) + if(busy) + user << "\The [src] is busy. Please wait for completion of previous operation." + return 1 + if(default_deconstruction_screwdriver(user, I)) + return + if(default_deconstruction_crowbar(user, I)) + return + if(default_part_replacement(user, I)) + return + + if(istype(I,/obj/item/weapon/disk/limb)) + var/obj/item/weapon/disk/limb/D = I + if(!D.company || !(D.company in all_robolimbs)) + user << "This disk seems to be corrupted!" + else + user << "Installing blueprint files for [D.company]..." + if(do_after(user,50,src)) + var/datum/robolimb/R = all_robolimbs[D.company] + R.unavailable_to_build = 0 + user << "Installed [D.company] blueprints!" + qdel(I) + return + + if(istype(I,/obj/item/stack/material)) + var/obj/item/stack/material/S = I + if(!(S.material.name in materials)) + user << "The [src] doesn't accept [S.material]!" + return + + var/sname = "[S.name]" + var/amnt = S.perunit + if(materials[S.material.name] + amnt <= res_max_amount) + if(S && S.amount >= 1) + var/count = 0 + overlays += "pros-load-metal" + spawn(10) + overlays -= "pros-load-metal" + while(materials[S.material.name] + amnt <= res_max_amount && S.amount >= 1) + materials[S.material.name] += amnt + S.use(1) + count++ + user << "You insert [count] [sname] into the fabricator." + update_busy() + else + user << "The fabricator cannot hold more [sname]." + + return + + ..() + +/obj/machinery/pros_fabricator/emag_act(var/remaining_charges, var/mob/user) + switch(emagged) + if(0) + emagged = 0.5 + visible_message("\icon[src] [src] beeps: \"DB error \[Code 0x00F1\]\"") + sleep(10) + visible_message("\icon[src] [src] beeps: \"Attempting auto-repair\"") + sleep(15) + visible_message("\icon[src] [src] beeps: \"User DB corrupted \[Code 0x00FA\]. Truncating data structure...\"") + sleep(30) + visible_message("\icon[src] [src] beeps: \"User DB truncated. Please contact your [company_name] system operator for future assistance.\"") + req_access = null + emagged = 1 + return 1 + if(0.5) + visible_message("\icon[src] [src] beeps: \"DB not responding \[Code 0x0003\]...\"") + if(1) + visible_message("\icon[src] [src] beeps: \"No records in User DB\"") + +/obj/machinery/pros_fabricator/proc/update_busy() + if(queue.len) + if(can_build(queue[1])) + busy = 1 + else + busy = 0 + else + busy = 0 + +/obj/machinery/pros_fabricator/proc/add_to_queue(var/index) + var/datum/design/D = files.known_designs[index] + queue += D + update_busy() + +/obj/machinery/pros_fabricator/proc/remove_from_queue(var/index) + if(index == 1) + progress = 0 + queue.Cut(index, index + 1) + update_busy() + +/obj/machinery/pros_fabricator/proc/can_build(var/datum/design/D) + for(var/M in D.materials) + if(materials[M] < D.materials[M]) + return 0 + return 1 + +/obj/machinery/pros_fabricator/proc/check_build() + if(!queue.len) + progress = 0 + return + var/datum/design/D = queue[1] + if(!can_build(D)) + progress = 0 + return + if(D.time > progress) + return + for(var/M in D.materials) + materials[M] = max(0, materials[M] - D.materials[M] * mat_efficiency) + if(D.build_path) + var/obj/new_item = D.Fabricate(get_step(get_turf(src), src.dir), src) + visible_message("\The [src] pings, indicating that \the [D] is complete.", "You hear a ping.") + if(mat_efficiency != 1) + if(new_item.matter && new_item.matter.len > 0) + for(var/i in new_item.matter) + new_item.matter[i] = new_item.matter[i] * mat_efficiency + remove_from_queue(1) + +/obj/machinery/pros_fabricator/proc/get_queue_names() + . = list() + for(var/i = 2 to queue.len) + var/datum/design/D = queue[i] + . += D.name + +/obj/machinery/pros_fabricator/proc/get_build_options() + . = list() + for(var/i = 1 to files.known_designs.len) + var/datum/design/D = files.known_designs[i] + if(D.build_path && (D.build_type & PROSFAB)) + . += list(list("name" = D.name, "id" = i, "category" = D.category, "resourses" = get_design_resourses(D), "time" = get_design_time(D))) + +/obj/machinery/pros_fabricator/proc/get_design_resourses(var/datum/design/D) + var/list/F = list() + for(var/T in D.materials) + F += "[capitalize(T)]: [D.materials[T] * mat_efficiency]" + return english_list(F, and_text = ", ") + +/obj/machinery/pros_fabricator/proc/get_design_time(var/datum/design/D) + return time2text(round(10 * D.time / speed), "mm:ss") + +/obj/machinery/pros_fabricator/proc/update_categories() + categories = list() + for(var/datum/design/D in files.known_designs) + if(!D.build_path || !(D.build_type & PROSFAB)) + continue + categories |= D.category + if(!category || !(category in categories)) + category = categories[1] + +/obj/machinery/pros_fabricator/proc/get_materials() + . = list() + for(var/T in materials) + . += list(list("mat" = capitalize(T), "amt" = materials[T])) + +/obj/machinery/pros_fabricator/proc/eject_materials(var/material, var/amount) // 0 amount = 0 means ejecting a full stack; -1 means eject everything + var/recursive = amount == -1 ? 1 : 0 + var/matstring = lowertext(material) + var/material/M = get_material_by_name(matstring) + + var/obj/item/stack/material/S = M.place_sheet(get_turf(src)) + if(amount <= 0) + amount = S.max_amount + var/ejected = min(round(materials[matstring] / S.perunit), amount) + S.amount = min(ejected, amount) + if(S.amount <= 0) + qdel(S) + return + materials[matstring] -= ejected * S.perunit + if(recursive && materials[matstring] >= S.perunit) + eject_materials(matstring, -1) + update_busy() + +/obj/machinery/pros_fabricator/proc/sync() + sync_message = "Error: no console found." + for(var/obj/machinery/computer/rdconsole/RDC in get_area_all_atoms(get_area(src))) + if(!RDC.sync) + continue + for(var/datum/tech/T in RDC.files.known_tech) + files.AddTech2Known(T) + for(var/datum/design/D in RDC.files.known_designs) + files.AddDesign2Known(D) + files.RefreshResearch() + sync_message = "Sync complete." + update_categories() diff --git a/code/game/mecha/medical/odysseus.dm b/code/game/mecha/medical/odysseus.dm index ede4ccf410..65ed711f2f 100644 --- a/code/game/mecha/medical/odysseus.dm +++ b/code/game/mecha/medical/odysseus.dm @@ -101,8 +101,6 @@ holder = patient.hud_list[STATUS_HUD] if(patient.stat == 2) holder.icon_state = "huddead" - else if(patient.status_flags & XENO_HOST) - holder.icon_state = "hudxeno" else if(foundVirus) holder.icon_state = "hudill" else if(patient.has_brain_worms()) diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index 65fa2ceb6a..fc764efd02 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -17,6 +17,7 @@ var/global/list/image/splatter_cache=list() var/base_icon = 'icons/effects/blood.dmi' blood_DNA = list() var/basecolor="#A10808" // Color when wet. + var/synthblood = 0 var/list/datum/disease2/disease/virus2 = list() var/amount = 5 var/drytime @@ -61,9 +62,9 @@ var/global/list/image/splatter_cache=list() /obj/effect/decal/cleanable/blood/update_icon() if(basecolor == "rainbow") basecolor = "#[get_random_colour(1)]" color = basecolor - if(basecolor == SYNTH_BLOOD_COLOUR) - name = "oil" - desc = "It's black and greasy." + if(synthblood) + name = "synthetic blood" + desc = "It's quite greasy." else name = initial(name) desc = initial(desc) @@ -181,7 +182,7 @@ var/global/list/image/splatter_cache=list() layer = 2 icon = 'icons/effects/blood.dmi' icon_state = "gibbl5" - random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6") + random_icon_states = list("gib1", "gib2", "gib3", "gib5", "gib6") var/fleshcolor = "#FFFFFF" /obj/effect/decal/cleanable/blood/gibs/update_icon() diff --git a/code/game/objects/effects/decals/posters/bs12.dm b/code/game/objects/effects/decals/posters/bs12.dm index a727e2804c..6b5ad182c4 100644 --- a/code/game/objects/effects/decals/posters/bs12.dm +++ b/code/game/objects/effects/decals/posters/bs12.dm @@ -91,7 +91,7 @@ /datum/poster/bay_19 icon_state="bsposter19" - name = "Respect a Unathi" + name = "Respect an Unathi" desc = "This poster depicts a well dressed looking Unathi receiving a prestigious award. It appears to espouse greater co-operation and harmony between the two races." /datum/poster/bay_20 @@ -117,7 +117,7 @@ /datum/poster/bay_24 icon_state="bsposter24" name = "Responsible medbay habits, No #259" - desc = "A poster with a nervous looking geneticist on it states; \"Friends Don't Tell Friends They're Clones. It can cause severe and irreparable emotional trauma. Always do the right thing and never tell them that they were dead.\"" + desc = "A poster with a nervous looking geneticist on it states; \"Friends Tell Friends They're Clones. It can cause severe and irreparable emotional trauma if a person is not properly informed of their recent demise. Always follow your contractual obligation and inform them of their recent rejuvenation.\"" /datum/poster/bay_25 icon_state="bsposter25" diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 2934e5d68d..4d78f5aa9d 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -453,6 +453,9 @@ var/list/global/slot_flags_enumeration = list( M.attack_log += "\[[time_stamp()]\] Attacked by [user.name] ([user.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)])" msg_admin_attack("[user.name] ([user.ckey]) attacked [M.name] ([M.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)]) (JMP)") //BS12 EDIT ALG + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.do_attack_animation(M) + src.add_fingerprint(user) //if((CLUMSY in user.mutations) && prob(50)) // M = user @@ -481,7 +484,7 @@ var/list/global/slot_flags_enumeration = list( eyes.damage += rand(3,4) if(eyes.damage >= eyes.min_bruised_damage) if(M.stat != 2) - if(!(eyes.status & ORGAN_ROBOT)) //robot eyes bleeding might be a bit silly + if(!(eyes.robotic >= ORGAN_ROBOT)) //robot eyes bleeding might be a bit silly M << "Your eyes start to bleed profusely!" if(prob(50)) if(M.stat != 2) @@ -633,6 +636,3 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. /obj/item/proc/pwr_drain() return 0 // Process Kill -/obj/item/proc/resolve_attackby(atom/A, mob/source) - return A.attackby(src,source) - diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index 0024a05c62..ae18aed86c 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -31,14 +31,17 @@ last_used = world.time times_used = max(0,round(times_used)) //sanity - -/obj/item/device/flash/attack(mob/living/M as mob, mob/user as mob) +//attack_as_weapon +/obj/item/device/flash/attack(mob/living/M, mob/living/user, var/target_zone) if(!user || !M) return //sanity M.attack_log += text("\[[time_stamp()]\] Has been flashed (attempt) with [src.name] by [user.name] ([user.ckey])") user.attack_log += text("\[[time_stamp()]\] Used the [src.name] to flash [M.name] ([M.ckey])") msg_admin_attack("[user.name] ([user.ckey]) Used the [src.name] to flash [M.name] ([M.ckey]) (JMP)") + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.do_attack_animation(M) + if(!clown_check(user)) return if(broken) user << "\The [src] is broken." @@ -117,6 +120,8 @@ /obj/item/device/flash/attack_self(mob/living/carbon/user as mob, flag = 0, emp = 0) if(!user || !clown_check(user)) return + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if(broken) user.show_message("The [src.name] is broken", 2) return @@ -136,7 +141,6 @@ else //can only use it 5 times a minute user.show_message("*click* *click*", 2) return - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) flick("flash2", src) if(user && isrobot(user)) @@ -184,7 +188,8 @@ icon_state = "sflash" origin_tech = list(TECH_MAGNET = 2, TECH_COMBAT = 1) -/obj/item/device/flash/synthetic/attack(mob/living/M as mob, mob/user as mob) +//attack_as_weapon +/obj/item/device/flash/synthetic/attack(mob/living/M, mob/living/user, var/target_zone) ..() if(!broken) broken = 1 diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index 83ac8851dd..c3fcf893ac 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -79,6 +79,8 @@ user << "\The [M]'s pupils narrow slightly, but are still very dilated." else user << "\The [M]'s pupils narrow." + + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) //can be used offensively flick("flash", M.flash) else return ..() diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm index e7cb6f3475..cbc60670e7 100644 --- a/code/game/objects/items/devices/multitool.dm +++ b/code/game/objects/items/devices/multitool.dm @@ -21,3 +21,13 @@ origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1) var/obj/machinery/telecomms/buffer // simple machine buffer for device linkage var/obj/machinery/clonepod/connecting //same for cryopod linkage + var/obj/machinery/connectable //Used to connect machinery, currently only used by Xenobio2. + +/obj/item/device/multitool/attack_self(mob/user) + var/clear = alert("Do you want to clear the buffers on the [src]?",, "Yes", "No",) + if(clear == "Yes") + buffer = null + connecting = null + connectable = null + else + ..() \ No newline at end of file diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index ef875940ad..a4b159b734 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -75,7 +75,7 @@ REAGENT SCANNER for(var/obj/item/organ/external/org in damaged) user.show_message(text(" [][]: [][] - []", capitalize(org.name), - (org.status & ORGAN_ROBOT) ? "(Cybernetic)" : "", + (org.robotic >= ORGAN_ROBOT) ? "(Cybernetic)" : "", (org.brute_dam > 0) ? "[org.brute_dam]" : 0, (org.status & ORGAN_BLEEDING)?"\[Bleeding\]":"", (org.burn_dam > 0) ? "[org.burn_dam]" : 0),1) diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index 3ccf1fb3a9..28f3a1def6 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -10,11 +10,7 @@ icon = 'icons/obj/decals.dmi' icon_state = "shock" -/obj/item/borg/stun/attack(var/mob/living/M, var/mob/living/silicon/robot/user) - - if(!istype(M)) - return - +/obj/item/borg/stun/apply_hit_effect(mob/living/M, mob/living/silicon/robot/user, var/hit_zone) // How the Hell. if(!istype(user)) var/mob/living/temp = user @@ -23,18 +19,16 @@ qdel(src) return - M.attack_log += text("\[[time_stamp()]\] Has been attacked with [src.name] by [user.name] ([user.ckey])") - user.attack_log += text("\[[time_stamp()]\] Used the [src.name] to attack [M.name] ([M.ckey])") - msg_admin_attack("[user.name] ([user.ckey]) used the [src.name] to attack [M.name] ([M.ckey]) (JMP)") + user.visible_message("\The [user] has prodded \the [M] with \a [src]!") if(!user.cell || !user.cell.checked_use(1250)) //Slightly more than a baton. - user.visible_message("\The [user] has prodded \the [M] with its arm!") return - if (M.stuttering < 5) - M.stuttering = 5 - M.stun_effect_act(0, 70, check_zone(user.zone_sel.selecting), src) - user.visible_message("\The [user] has prodded \the [M] with \a [src]!") + playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) + + M.apply_effect(5, STUTTER) + M.stun_effect_act(0, 70, check_zone(hit_zone), src) + if(ishuman(M)) var/mob/living/carbon/human/H = M H.forcesay(hit_appends) diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm index 11c312048d..ebdd95791d 100644 --- a/code/game/objects/items/robot/robot_parts.dm +++ b/code/game/objects/items/robot/robot_parts.dm @@ -15,47 +15,37 @@ /obj/item/robot_parts/New(var/newloc, var/model) ..(newloc) - if(model_info && model) - model_info = model - var/datum/robolimb/R = all_robolimbs[model] - if(R) - name = "[R.company] [initial(name)]" - desc = "[R.desc]" - if(icon_state in icon_states(R.icon)) - icon = R.icon - else - name = "robot [initial(name)]" /obj/item/robot_parts/l_arm - name = "left arm" + name = "cyborg left arm" desc = "A skeletal limb wrapped in pseudomuscles, with a low-conductivity case." icon_state = "l_arm" part = list(BP_L_ARM, BP_L_HAND) model_info = 1 /obj/item/robot_parts/r_arm - name = "right arm" + name = "cyborg right arm" desc = "A skeletal limb wrapped in pseudomuscles, with a low-conductivity case." icon_state = "r_arm" part = list(BP_R_ARM, BP_R_HAND) model_info = 1 /obj/item/robot_parts/l_leg - name = "left leg" + name = "cyborg left leg" desc = "A skeletal limb wrapped in pseudomuscles, with a low-conductivity case." icon_state = "l_leg" part = list(BP_L_LEG, BP_L_FOOT) model_info = 1 /obj/item/robot_parts/r_leg - name = "right leg" + name = "cyborg leg" desc = "A skeletal limb wrapped in pseudomuscles, with a low-conductivity case." icon_state = "r_leg" part = list(BP_R_LEG, BP_R_FOOT) model_info = 1 /obj/item/robot_parts/chest - name = "chest" + name = "cyborg chest" desc = "A heavily reinforced case containing cyborg logic boards, with space for a standard power cell." icon_state = "chest" part = list(BP_GROIN,BP_TORSO) @@ -63,7 +53,7 @@ var/obj/item/weapon/cell/cell = null /obj/item/robot_parts/head - name = "head" + name = "cyborg head" desc = "A standard reinforced braincase, with spine-plugged neural socket and sensor gimbals." icon_state = "head" part = list(BP_HEAD) diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index c55fd587c0..2ddbd4e330 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -24,6 +24,10 @@ var/mob/living/carbon/human/H = M var/obj/item/organ/external/affecting = H.get_organ(user.zone_sel.selecting) + if(!affecting) + user << "No body part there to work on!" + return 1 + if(affecting.organ_tag == BP_HEAD) if(H.head && istype(H.head,/obj/item/clothing/head/helmet/space)) user << "You can't apply [src] through [H.head]!" @@ -33,8 +37,13 @@ user << "You can't apply [src] through [H.wear_suit]!" return 1 - if(affecting.status & ORGAN_ROBOT) - user << "This isn't useful at all on a robotic limb.." + if(affecting.robotic == ORGAN_ROBOT) + user << "This isn't useful at all on a robotic limb." + return 1 + + if(affecting.robotic >= ORGAN_LIFELIKE) + user << "You apply the [src], but it seems to have no effect..." + use(1) return 1 H.UpdateDamageIcon() diff --git a/code/game/objects/items/stacks/nanopaste.dm b/code/game/objects/items/stacks/nanopaste.dm index a9ec052102..1ff55adf85 100644 --- a/code/game/objects/items/stacks/nanopaste.dm +++ b/code/game/objects/items/stacks/nanopaste.dm @@ -27,19 +27,12 @@ var/mob/living/carbon/human/H = M var/obj/item/organ/external/S = H.get_organ(user.zone_sel.selecting) - if(S.open == 1) - if (S && (S.status & ORGAN_ROBOT)) - if(S.get_damage()) - S.heal_damage(15, 15, robo_repair = 1) - H.updatehealth() - use(1) - user.visible_message("\The [user] applies some nanite paste at[user != M ? " \the [M]'s" : " \the"][S.name] with \the [src].",\ - "You apply some nanite paste at [user == M ? "your" : "[M]'s"] [S.name].") - else - user << "Nothing to fix here." + if (S && (S.robotic >= ORGAN_ROBOT)) + if(S.robo_repair(15, "omni", 0, src, user)) + use(1) + user.visible_message("\The [user] applies some nanite paste on [user != M ? "\the [M]'s" : "their"] [S.name].",\ + "You apply some nanite paste on [user == M ? "your" : "[M]'s"] [S.name].") else if (can_operate(H)) if (do_surgery(H,user,src)) return - else - user << "Nothing to fix in here." diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm index 9926a14330..669c32955f 100644 --- a/code/game/objects/items/stacks/tiles/tile_types.dm +++ b/code/game/objects/items/stacks/tiles/tile_types.dm @@ -4,6 +4,7 @@ * Grass * Wood * Carpet + * Blue Carpet * Linoleum */ @@ -70,6 +71,12 @@ throw_range = 20 flags = 0 +/obj/item/stack/tile/carpet/blue + name = "blue carpet" + singular_name = "blue carpet" + desc = "A piece of blue carpet. It is the same size as a normal floor tile!" + icon_state = "tile-bluecarpet" + /obj/item/stack/tile/floor name = "floor tile" singular_name = "floor tile" diff --git a/code/game/objects/items/trash.dm b/code/game/objects/items/trash.dm index 34181486e4..1d171d9dbe 100644 --- a/code/game/objects/items/trash.dm +++ b/code/game/objects/items/trash.dm @@ -9,11 +9,15 @@ /obj/item/trash/raisins name = "\improper 4no raisins" - icon_state= "4no_raisins" + icon_state = "4no_raisins" /obj/item/trash/candy name = "candy" - icon_state= "candy" + icon_state = "candy" + +/obj/item/trash/candy/proteinbar + name = "protein bar" + icon_state = "proteinbar" /obj/item/trash/cheesie name = "\improper Cheesie Honkers" diff --git a/code/game/objects/items/weapons/circuitboards/machinery/jukebox.dm b/code/game/objects/items/weapons/circuitboards/machinery/jukebox.dm new file mode 100644 index 0000000000..4b6296c886 --- /dev/null +++ b/code/game/objects/items/weapons/circuitboards/machinery/jukebox.dm @@ -0,0 +1,13 @@ +#ifndef T_BOARD +#error T_BOARD macro is not defined but we need it! +#endif + +/obj/item/weapon/circuitboard/jukebox + name = T_BOARD("jukebox") + build_path = "/obj/machinery/media/jukebox" + board_type = "machine" + origin_tech = list(TECH_MAGNET = 2, TECH_DATA = 1) + req_components = list( + /obj/item/weapon/stock_parts/capacitor = 1, + /obj/item/weapon/stock_parts/console_screen = 1, + /obj/item/stack/cable_coil = 5) diff --git a/code/game/objects/items/weapons/circuitboards/machinery/research.dm b/code/game/objects/items/weapons/circuitboards/machinery/research.dm index bf0eb357f4..c871bd3dc5 100644 --- a/code/game/objects/items/weapons/circuitboards/machinery/research.dm +++ b/code/game/objects/items/weapons/circuitboards/machinery/research.dm @@ -61,3 +61,14 @@ obj/item/weapon/circuitboard/rdserver /obj/item/weapon/stock_parts/manipulator = 1, /obj/item/weapon/stock_parts/micro_laser = 1, /obj/item/weapon/stock_parts/console_screen = 1) + +/obj/item/weapon/circuitboard/prosthetics + name = "Circuit board (Prosthetics Fabricator)" + build_path = "/obj/machinery/pros_fabricator" + board_type = "machine" + origin_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 3) + req_components = list( + /obj/item/weapon/stock_parts/matter_bin = 2, + /obj/item/weapon/stock_parts/manipulator = 1, + /obj/item/weapon/stock_parts/micro_laser = 1, + /obj/item/weapon/stock_parts/console_screen = 1) \ No newline at end of file diff --git a/code/game/objects/items/weapons/clown_items.dm b/code/game/objects/items/weapons/clown_items.dm index 0ccb1f1a24..d15b78963d 100644 --- a/code/game/objects/items/weapons/clown_items.dm +++ b/code/game/objects/items/weapons/clown_items.dm @@ -34,6 +34,10 @@ //So this is a workaround. This also makes more sense from an IC standpoint. ~Carn if(user.client && (target in user.client.screen)) user << "You need to take that [target.name] off before cleaning it." + else if(istype(target,/obj/effect/decal/cleanable/blood)) + user << "You scrub \the [target.name] out." + target.clean_blood() + return //Blood is a cleanable decal, therefore needs to be accounted for before all cleanable decals. else if(istype(target,/obj/effect/decal/cleanable)) user << "You scrub \the [target.name] out." qdel(target) @@ -49,9 +53,11 @@ target.clean_blood() return -/obj/item/weapon/soap/attack(mob/target as mob, mob/user as mob) - if(target && user && ishuman(target) && ishuman(user) && !target.stat && !user.stat && user.zone_sel &&user.zone_sel.selecting == O_MOUTH) +//attack_as_weapon +/obj/item/weapon/soap/attack(mob/living/target, mob/living/user, var/target_zone) + if(target && user && ishuman(target) && ishuman(user) && !user.incapacitated() && user.zone_sel &&user.zone_sel.selecting == "mouth" ) user.visible_message("\The [user] washes \the [target]'s mouth out with soap!") + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //prevent spam return ..() diff --git a/code/game/objects/items/weapons/dna_injector.dm b/code/game/objects/items/weapons/dna_injector.dm index 484c185a14..24f6ad53b3 100644 --- a/code/game/objects/items/weapons/dna_injector.dm +++ b/code/game/objects/items/weapons/dna_injector.dm @@ -114,6 +114,9 @@ if(!do_after(user,50)) return + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) + user.do_attack_animation(M) + M.visible_message("\The [M] has been injected with \the [src] by \the [user].") var/mob/living/carbon/human/H = M diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm index 294812513c..46e8e7ba26 100644 --- a/code/game/objects/items/weapons/explosives.dm +++ b/code/game/objects/items/weapons/explosives.dm @@ -46,6 +46,7 @@ if (ismob(target) || istype(target, /turf/unsimulated) || istype(target, /turf/simulated/shuttle) || istype(target, /obj/item/weapon/storage/) || istype(target, /obj/item/clothing/accessory/storage/) || istype(target, /obj/item/clothing/under)) return user << "Planting explosives..." + user.do_attack_animation(target) if(do_after(user, 50) && in_range(user, target)) user.drop_item() diff --git a/code/game/objects/items/weapons/gift_wrappaper.dm b/code/game/objects/items/weapons/gift_wrappaper.dm index 73c198c8ab..94775ceb36 100644 --- a/code/game/objects/items/weapons/gift_wrappaper.dm +++ b/code/game/objects/items/weapons/gift_wrappaper.dm @@ -61,7 +61,7 @@ qdel(src) /obj/item/weapon/a_gift/attack_self(mob/M as mob) - var/gift_type = pick(/obj/item/weapon/sord, + var/gift_type = pick( /obj/item/weapon/storage/wallet, /obj/item/weapon/storage/photo_album, /obj/item/weapon/storage/box/snappops, @@ -80,7 +80,6 @@ /obj/item/weapon/bikehorn, /obj/item/weapon/beach_ball, /obj/item/weapon/beach_ball/holoball, - /obj/item/weapon/banhammer, /obj/item/toy/balloon, /obj/item/toy/blink, /obj/item/toy/crossbow, diff --git a/code/game/objects/items/weapons/grenades/emgrenade.dm b/code/game/objects/items/weapons/grenades/emgrenade.dm index 632c82b474..91b2d8f7e1 100644 --- a/code/game/objects/items/weapons/grenades/emgrenade.dm +++ b/code/game/objects/items/weapons/grenades/emgrenade.dm @@ -1,5 +1,5 @@ /obj/item/weapon/grenade/empgrenade - name = "classic emp grenade" + name = "emp grenade" icon_state = "emp" item_state = "empgrenade" origin_tech = list(TECH_MATERIAL = 2, TECH_MAGNET = 3) @@ -12,7 +12,7 @@ /obj/item/weapon/grenade/empgrenade/low_yield name = "low yield emp grenade" - desc = "A weaker variant of the classic emp grenade" + desc = "A weaker variant of the EMP grenade" icon_state = "lyemp" item_state = "lyempgrenade" origin_tech = list(TECH_MATERIAL = 2, TECH_MAGNET = 3) diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm index 1868827b5b..c44c3e1688 100644 --- a/code/game/objects/items/weapons/handcuffs.dm +++ b/code/game/objects/items/weapons/handcuffs.dm @@ -74,6 +74,9 @@ msg_admin_attack("[key_name(user)] attempted to handcuff [key_name(H)]") feedback_add_details("handcuffs","H") + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.do_attack_animation(H) + user.visible_message("\The [user] has put [cuff_type] on \the [H]!") // Apply cuffs. @@ -166,4 +169,4 @@ var/last_chew = 0 item_state = null icon = 'icons/obj/bureaucracy.dmi' breakouttime = 200 - cuff_type = "duct tape" \ No newline at end of file + cuff_type = "duct tape" diff --git a/code/game/objects/items/weapons/implants/implanter.dm b/code/game/objects/items/weapons/implants/implanter.dm index 4f168556d2..655727a62a 100644 --- a/code/game/objects/items/weapons/implants/implanter.dm +++ b/code/game/objects/items/weapons/implants/implanter.dm @@ -30,18 +30,18 @@ if (!istype(M, /mob/living/carbon)) return if (user && src.imp) - for (var/mob/O in viewers(M, null)) - O.show_message("[user] is attemping to implant [M].", 1) + M.visible_message("[user] is attemping to implant [M].") + + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) + user.do_attack_animation(M) var/turf/T1 = get_turf(M) if (T1 && ((M == user) || do_after(user, 50))) if(user && M && (get_turf(M) == T1) && src && src.imp) - for (var/mob/O in viewers(M, null)) - O.show_message("[M] has been implanted by [user].", 1) + M.visible_message("[M] has been implanted by [user].") admin_attack_log(user, M, "Implanted using \the [src.name] ([src.imp.name])", "Implanted with \the [src.name] ([src.imp.name])", "used an implanter, [src.name] ([src.imp.name]), on") - user.show_message("You implanted the implant into [M].") if(src.imp.implanted(M)) src.imp.loc = M src.imp.imp_in = M diff --git a/code/game/objects/items/weapons/material/kitchen.dm b/code/game/objects/items/weapons/material/kitchen.dm index ff1d347c74..6672d488de 100644 --- a/code/game/objects/items/weapons/material/kitchen.dm +++ b/code/game/objects/items/weapons/material/kitchen.dm @@ -103,13 +103,6 @@ return return ..() -/obj/item/weapon/material/kitchen/utensil/knife/attack(target as mob, mob/living/user as mob) - if ((CLUMSY in user.mutations) && prob(50)) - user << "You somehow managed to cut yourself with \the [src]." - user.take_organ_damage(20) - return - return ..() - /obj/item/weapon/material/kitchen/utensil/knife/plastic default_material = "plastic" diff --git a/code/game/objects/items/weapons/material/material_weapons.dm b/code/game/objects/items/weapons/material/material_weapons.dm index 18181df6b0..fc9ac7eb32 100644 --- a/code/game/objects/items/weapons/material/material_weapons.dm +++ b/code/game/objects/items/weapons/material/material_weapons.dm @@ -64,9 +64,8 @@ processing_objects -= src ..() -/obj/item/weapon/material/attack() - if(!..()) - return +/obj/item/weapon/material/apply_hit_effect() + ..() if(!unbreakable) if(material.is_brittle()) health = 0 diff --git a/code/game/objects/items/weapons/material/misc.dm b/code/game/objects/items/weapons/material/misc.dm index dcd33fa172..91d97274f6 100644 --- a/code/game/objects/items/weapons/material/misc.dm +++ b/code/game/objects/items/weapons/material/misc.dm @@ -65,7 +65,6 @@ icon = 'icons/obj/weapons.dmi' icon_state = "hoe" item_state = "hoe" - flags = CONDUCT | NOBLUDGEON force_divisor = 0.25 // 5 with weight 20 (steel) thrown_force_divisor = 0.25 // as above w_class = 2 diff --git a/code/game/objects/items/weapons/material/shards.dm b/code/game/objects/items/weapons/material/shards.dm index e708cc0765..f4b763c5e9 100644 --- a/code/game/objects/items/weapons/material/shards.dm +++ b/code/game/objects/items/weapons/material/shards.dm @@ -85,7 +85,7 @@ var/picked = pick(check) var/obj/item/organ/external/affecting = H.get_organ(picked) if(affecting) - if(affecting.status & ORGAN_ROBOT) + if(affecting.robotic >= ORGAN_ROBOT) return if(affecting.take_damage(5, 0)) H.UpdateDamageIcon() diff --git a/code/game/objects/items/weapons/policetape.dm b/code/game/objects/items/weapons/policetape.dm index e3c1268ea0..4ddd60526c 100644 --- a/code/game/objects/items/weapons/policetape.dm +++ b/code/game/objects/items/weapons/policetape.dm @@ -9,6 +9,20 @@ var/tape_type = /obj/item/tape var/icon_base + var/apply_tape = FALSE + +/obj/item/taperoll/initialize() + ..() + if(apply_tape) + var/turf/T = get_turf(src) + if(!T) + return + var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in T + if(airlock) + afterattack(airlock, null, TRUE) + qdel(src) + + var/list/image/hazard_overlays var/list/tape_roll_applications = list() @@ -63,6 +77,9 @@ var/list/tape_roll_applications = list() tape_type = /obj/item/tape/engineering icon_base = "engineering" +/obj/item/taperoll/engineering/applied + apply_tape = TRUE + /obj/item/tape/engineering name = "engineering tape" desc = "A length of engineering tape. Better not cross it." @@ -220,8 +237,7 @@ var/list/tape_roll_applications = list() if (istype(A, /obj/machinery/door/airlock)) var/turf/T = get_turf(A) - var/obj/item/tape/P = new tape_type(T.x,T.y,T.z) - P.loc = locate(T.x,T.y,T.z) + var/obj/item/tape/P = new tape_type(T) P.update_icon() P.layer = 3.2 user << "You finish placing \the [src]." diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index 478272b8b2..c0469cd9f8 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -187,7 +187,8 @@ /obj/item/device/flash, /obj/item/weapon/flame/lighter, /obj/item/weapon/reagent_containers/food/snacks/donut/, - /obj/item/ammo_magazine + /obj/item/ammo_magazine, + /obj/item/weapon/gun/projectile/colt/detective ) /obj/item/weapon/storage/belt/soulstone diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm index a4d771aa81..b7e55cddb8 100644 --- a/code/game/objects/items/weapons/stunbaton.dm +++ b/code/game/objects/items/weapons/stunbaton.dm @@ -12,6 +12,7 @@ w_class = 3 origin_tech = list(TECH_COMBAT = 2) attack_verb = list("beaten") + var/lightcolor = "#FF6A00" var/stunforce = 0 var/agonyforce = 60 var/status = 0 //whether the thing is on or not @@ -52,7 +53,7 @@ icon_state = "[initial(name)]" if(icon_state == "[initial(name)]_active") - set_light(1.5, 1, "#FF6A00") + set_light(1.5, 1, lightcolor) else set_light(0) @@ -102,73 +103,52 @@ user << "[src] is out of charge." add_fingerprint(user) - /obj/item/weapon/melee/baton/attack(mob/M, mob/user) if(status && (CLUMSY in user.mutations) && prob(50)) user << "You accidentally hit yourself with the [src]!" user.Weaken(30) deductcharge(hitcost) return + return ..() - if(isrobot(M)) - ..() - return +/obj/item/weapon/melee/baton/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) + if(isrobot(target)) + return ..() var/agony = agonyforce var/stun = stunforce - var/mob/living/L = M + var/obj/item/organ/external/affecting = null + if(ishuman(target)) + var/mob/living/carbon/human/H = target + affecting = H.get_organ(hit_zone) - var/target_zone = check_zone(user.zone_sel.selecting) if(user.a_intent == I_HURT) - if (!..()) //item/attack() does it's own messaging and logs - return 0 // item/attack() will return 1 if they hit, 0 if they missed. + . = ..() + //whacking someone causes a much poorer electrical contact than deliberately prodding them. + agony *= 0.5 stun *= 0.5 - if(status) //Checks to see if the stunbaton is on. - agony *= 0.5 //whacking someone causes a much poorer contact than prodding them. + else if(!status) + if(affecting) + target.visible_message("[target] has been prodded in the [affecting.name] with [src] by [user]. Luckily it was off.") else - agony = 0 //Shouldn't really stun if it's off, should it? - //we can't really extract the actual hit zone from ..(), unfortunately. Just act like they attacked the area they intended to. + target.visible_message("[target] has been prodded with [src] by [user]. Luckily it was off.") else - //copied from human_defense.dm - human defence code should really be refactored some time. - if (ishuman(L)) - user.lastattacked = L //are these used at all, if we have logs? - L.lastattacker = user - - if (user != L) // Attacking yourself can't miss - target_zone = get_zone_with_miss_chance(user.zone_sel.selecting, L) - - if(!target_zone) - L.visible_message("\The [user] misses [L] with \the [src]!") - return 0 - - var/mob/living/carbon/human/H = L - var/obj/item/organ/external/affecting = H.get_organ(target_zone) - if (affecting) - if(!status) - L.visible_message("[L] has been prodded in the [affecting.name] with [src] by [user]. Luckily it was off.") - return 1 - else - H.visible_message("[L] has been prodded in the [affecting.name] with [src] by [user]!") + if(affecting) + target.visible_message("[target] has been prodded in the [affecting.name] with [src] by [user]!") else - if(!status) - L.visible_message("[L] has been prodded with [src] by [user]. Luckily it was off.") - return 1 - else - L.visible_message("[L] has been prodded with [src] by [user]!") + target.visible_message("[target] has been prodded with [src] by [user]!") + playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) //stun effects - L.stun_effect_act(stun, agony, target_zone, src) + if(status) + target.stun_effect_act(stun, agony, hit_zone, src) + msg_admin_attack("[key_name(user)] stunned [key_name(target)] with the [src].") - playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) - msg_admin_attack("[key_name(user)] stunned [key_name(L)] with the [src].") + deductcharge(hitcost) - deductcharge(hitcost) - - if(ishuman(L)) - var/mob/living/carbon/human/H = L - H.forcesay(hit_appends) - - return 1 + if(ishuman(target)) + var/mob/living/carbon/human/H = target + H.forcesay(hit_appends) /obj/item/weapon/melee/baton/emp_act(severity) if(bcell) @@ -176,6 +156,9 @@ ..() //secborg stun baton module +/obj/item/weapon/melee/baton/robot + hitcost = 500 + /obj/item/weapon/melee/baton/robot/attack_self(mob/user) //try to find our power cell var/mob/living/silicon/robot/R = loc diff --git a/code/game/objects/items/weapons/surgery_limbattachment.dm b/code/game/objects/items/weapons/surgery_limbattachment.dm deleted file mode 100644 index 4ba10524f2..0000000000 --- a/code/game/objects/items/weapons/surgery_limbattachment.dm +++ /dev/null @@ -1,71 +0,0 @@ -/obj/item/robot_parts/attack(mob/living/carbon/human/M as mob, mob/living/carbon/user as mob) - var/limbloc = null - - if(!istype(M)) - return ..() - - if(!((locate(/obj/machinery/optable, M.loc) && M.resting) || (locate(/obj/structure/bed/roller, M.loc) && (M.buckled || M.lying || M.weakened || M.stunned || M.paralysis || M.sleeping || M.stat)) && prob(75) || (locate(/obj/structure/table/, M.loc) && (M.lying || M.weakened || M.stunned || M.paralysis || M.sleeping || M.stat) && prob(66)))) - return ..() - - if(!istype(M, /mob/living/carbon/human)) - return ..() - - if((user.zone_sel.selecting == "l_arm") && (istype(src, /obj/item/robot_parts/l_arm))) - limbloc = "l_hand" - else if((user.zone_sel.selecting == "r_arm") && (istype(src, /obj/item/robot_parts/r_arm))) - limbloc = "r_hand" - else if((user.zone_sel.selecting == "r_leg") && (istype(src, /obj/item/robot_parts/r_leg))) - limbloc = "r_foot" - else if((user.zone_sel.selecting == "l_leg") && (istype(src, /obj/item/robot_parts/l_leg))) - limbloc = "l_foot" - else - user << "That doesn't fit there!" - return ..() - - var/mob/living/carbon/human/H = M - var/datum/organ/external/S = H.organs[user.zone_sel.selecting] - if(S.status & ORGAN_DESTROYED) - if(!(S.status & ORGAN_ATTACHABLE)) - user << "The wound is not ready for a replacement!" - return 0 - if(M != user) - M.visible_message( \ - "\The [user] is beginning to attach \the [src] where [H]'s [S.display_name] used to be.", \ - "\The [user] begins to attach \the [src] where your [S.display_name] used to be.") - else - M.visible_message( \ - "\The [user] begins to attach a robotic limb where \his [S.display_name] used to be with [src].", \ - "You begin to attach \the [src] where your [S.display_name] used to be.") - - if(do_mob(user, H, 100)) - if(M != user) - M.visible_message( \ - "\The [user] finishes attaching [H]'s new [S.display_name].", \ - "\The [user] finishes attaching your new [S.display_name].") - else - M.visible_message( \ - "\The [user] finishes attaching \his new [S.display_name].", \ - "You finish attaching your new [S.display_name].") - - if(H == user && prob(25)) - user << "You mess up!" - S.take_damage(15) - - S.status &= ~ORGAN_BROKEN - S.status &= ~ORGAN_SPLINTED - S.status &= ~ORGAN_ATTACHABLE - S.status &= ~ORGAN_DESTROYED - S.status |= ORGAN_ROBOT - var/datum/organ/external/T = H.organs["[limbloc]"] - T.status &= ~ORGAN_BROKEN - T.status &= ~ORGAN_SPLINTED - T.status &= ~ORGAN_ATTACHABLE - T.status &= ~ORGAN_DESTROYED - T.status |= ORGAN_ROBOT - H.update_body() - M.updatehealth() - M.UpdateDamageIcon() - qdel(src) - - return 1 - return 0 diff --git a/code/game/objects/items/weapons/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm index c773f3da38..b289e79257 100644 --- a/code/game/objects/items/weapons/swords_axes_etc.dm +++ b/code/game/objects/items/weapons/swords_axes_etc.dm @@ -1,18 +1,10 @@ /* Weapons * Contains: - * Banhammer * Sword * Classic Baton */ -/* - * Banhammer - */ -/obj/item/weapon/banhammer/attack(mob/M as mob, mob/user as mob) - M << " You have been banned FOR NO REISIN by [user]" - user << " You have BANNED [M]" - -/* +/* * Classic Baton */ /obj/item/weapon/melee/classic_baton @@ -34,33 +26,7 @@ else user.take_organ_damage(2*force) return -/*this is already called in ..() - src.add_fingerprint(user) - M.attack_log += text("\[[time_stamp()]\] Has been attacked with [src.name] by [user.name] ([user.ckey])") - user.attack_log += text("\[[time_stamp()]\] Used the [src.name] to attack [M.name] ([M.ckey])") - - log_attack("[user.name] ([user.ckey]) attacked [M.name] ([M.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)])") -*/ - if (user.a_intent == I_HURT) - if(!..()) return - //playsound(src.loc, "swing_hit", 50, 1, -1) - if (M.stuttering < 8 && (!(HULK in M.mutations)) /*&& (!istype(H:wear_suit, /obj/item/clothing/suit/judgerobe))*/) - M.stuttering = 8 - M.Stun(8) - M.Weaken(8) - for(var/mob/O in viewers(M)) - if (O.client) O.show_message("\The [M] has been beaten with \the [src] by [user]!", 1, "You hear someone fall", 2) - else - playsound(src.loc, 'sound/weapons/Genhit.ogg', 50, 1, -1) - M.Stun(5) - M.Weaken(5) - M.attack_log += text("\[[time_stamp()]\] Has been attacked with [src.name] by [user.name] ([user.ckey])") - user.attack_log += text("\[[time_stamp()]\] Used the [src.name] to attack [M.name] ([M.ckey])") - msg_admin_attack("[key_name(user)] attacked [key_name(user)] with [src.name] (INTENT: [uppertext(user.a_intent)])") - src.add_fingerprint(user) - - for(var/mob/O in viewers(M)) - if (O.client) O.show_message("\The [M] has been stunned with \the [src] by [user]!", 1, "You hear someone fall", 2) + return ..() //Telescopic baton /obj/item/weapon/melee/telebaton diff --git a/code/game/objects/items/weapons/tools.dm b/code/game/objects/items/weapons/tools.dm index c1922b735e..b39fc0dd27 100644 --- a/code/game/objects/items/weapons/tools.dm +++ b/code/game/objects/items/weapons/tools.dm @@ -116,7 +116,7 @@ item_state = "cutters_yellow" /obj/item/weapon/wirecutters/attack(mob/living/carbon/C as mob, mob/user as mob) - if((C.handcuffed) && (istype(C.handcuffed, /obj/item/weapon/handcuffs/cable))) + if(user.a_intent == I_HELP && (C.handcuffed) && (istype(C.handcuffed, /obj/item/weapon/handcuffs/cable))) usr.visible_message("\The [usr] cuts \the [C]'s restraints with \the [src]!",\ "You cut \the [C]'s restraints with \the [src]!",\ "You hear cable being cut.") @@ -428,22 +428,18 @@ var/mob/living/carbon/human/H = A var/obj/item/organ/external/S = H.organs_by_name[user.zone_sel.selecting] - if(!S || !(S.status & ORGAN_ROBOT)) + if(!S || S.robotic < ORGAN_ROBOT || S.open == 3) return ..() - if(S.brute_dam) - if(S.brute_dam < ROBOLIMB_SELF_REPAIR_CAP) - S.heal_damage(15,0,0,1) - user.visible_message("\The [user] patches some dents on \the [H]'s [S.name] with \the [src].") - else if(S.open < 3) - user << "The damage is far too severe to patch over externally." - else - return ..() - else - user << "Nothing to fix!" - S.update_wounds() - return - return ..() + if(!welding) + user << "You'll need to turn [src] on to patch the damage on [H]'s [S.name]!" + return 1 + + if(S.robo_repair(15, BRUTE, "some dents", src, user)) + remove_fuel(1, user) + + else + return ..() /*/obj/item/weapon/combitool name = "combi-tool" diff --git a/code/game/objects/items/weapons/towels.dm b/code/game/objects/items/weapons/towels.dm new file mode 100644 index 0000000000..94c266142e --- /dev/null +++ b/code/game/objects/items/weapons/towels.dm @@ -0,0 +1,14 @@ +/obj/item/weapon/towel + name = "towel" + icon = 'icons/obj/weapons.dmi' + icon_state = "towel" + slot_flags = SLOT_HEAD | SLOT_BELT | SLOT_OCLOTHING + force = 3.0 + w_class = 3.0 + attack_verb = list("whipped") + hitsound = 'sound/weapons/towelwhip.ogg' + desc = "A soft cotton towel." + +/obj/item/weapon/towel/attack_self(mob/living/user as mob) + user.visible_message(text("[] uses [] to towel themselves off.", user, src)) + playsound(user, 'sound/weapons/towelwipe.ogg', 25, 1) diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm index 35302dc5dc..0f5cebc44d 100644 --- a/code/game/objects/items/weapons/weaponry.dm +++ b/code/game/objects/items/weapons/weaponry.dm @@ -1,18 +1,3 @@ -/obj/item/weapon/banhammer - desc = "banhammer" - name = "banhammer" - icon = 'icons/obj/items.dmi' - icon_state = "toyhammer" - slot_flags = SLOT_BELT - throwforce = 0 - w_class = 2.0 - throw_speed = 7 - throw_range = 15 - attack_verb = list("banned") - - suicide_act(mob/user) - viewers(user) << "[user] is hitting \himself with the [src.name]! It looks like \he's trying to ban \himself from life." - return (BRUTELOSS|FIRELOSS|TOXLOSS|OXYLOSS) /obj/item/weapon/nullrod name = "null rod" @@ -37,6 +22,9 @@ msg_admin_attack("[user.name] ([user.ckey]) attacked [M.name] ([M.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)]) (JMP)") + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.do_attack_animation(M) + if (!(istype(user, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey") user << "You don't have the dexterity to do this!" return @@ -68,27 +56,6 @@ user << "You hit the floor with the [src]." call(/obj/effect/rune/proc/revealrunes)(src) -/obj/item/weapon/sord - name = "\improper SORD" - desc = "This thing is so unspeakably shitty you are having a hard time even holding it." - icon_state = "sord" - item_state = "sord" - slot_flags = SLOT_BELT - force = 2 - throwforce = 1 - sharp = 1 - edge = 1 - w_class = 3 - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - - suicide_act(mob/user) - viewers(user) << "[user] is impaling \himself with the [src.name]! It looks like \he's trying to commit suicide." - return(BRUTELOSS) - -/obj/item/weapon/sord/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) - playsound(loc, 'sound/weapons/bladeslice.ogg', 50, 1, -1) - return ..() - /obj/item/weapon/energy_net name = "energy net" desc = "It's a net made of green energy." diff --git a/code/game/objects/random/random.dm b/code/game/objects/random/random.dm index e64d422a20..1127bf7510 100644 --- a/code/game/objects/random/random.dm +++ b/code/game/objects/random/random.dm @@ -108,7 +108,7 @@ prob(2);/obj/random/bomb_supply,\ prob(1);/obj/item/weapon/extinguisher,\ prob(1);/obj/item/clothing/gloves/fyellow,\ - prob(3);/obj/item/stack/cable_coil,\ + prob(3);/obj/item/stack/cable_coil/random,\ prob(2);/obj/random/toolbox,\ prob(2);/obj/item/weapon/storage/belt/utility,\ prob(5);/obj/random/tool,\ diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm index ad1797756c..375c6727ea 100644 --- a/code/game/objects/structures/bedsheet_bin.dm +++ b/code/game/objects/structures/bedsheet_bin.dm @@ -101,6 +101,78 @@ LINEN BINS icon_state = "sheetbrown" item_state = "sheetbrown" +/obj/item/weapon/bedsheet/ian + icon_state = "sheetian" + item_state = "bedsheet" + +/obj/item/weapon/bedsheet/double + icon_state = "doublesheet" + item_state = "bedsheet" + +/obj/item/weapon/bedsheet/bluedouble + icon_state = "doublesheetblue" + item_state = "sheetblue" + +/obj/item/weapon/bedsheet/greendouble + icon_state = "doublesheetgreen" + item_state = "sheetgreen" + +/obj/item/weapon/bedsheet/orangedouble + icon_state = "doublesheetorange" + item_state = "sheetorange" + +/obj/item/weapon/bedsheet/purpledouble + icon_state = "doublesheetpurple" + item_state = "sheetpurple" + +/obj/item/weapon/bedsheet/doublerainbow //all the way across the sky. + icon_state = "doublesheetrainbow" + item_state = "sheetrainbow" + +/obj/item/weapon/bedsheet/doublered + icon_state = "doublesheetred" + item_state = "sheetred" + +/obj/item/weapon/bedsheet/doubleyellow + icon_state = "doublesheetyellow" + item_state = "sheetyellow" + +/obj/item/weapon/bedsheet/doublemime + icon_state = "doublesheetmime" + item_state = "sheetmime" + +/obj/item/weapon/bedsheet/doubleclown + icon_state = "doublesheetclown" + item_state = "sheetclown" + +/obj/item/weapon/bedsheet/doublecaptain + icon_state = "doublesheetcaptain" + item_state = "sheetcaptain" + +/obj/item/weapon/bedsheet/doublerd + icon_state = "doublesheetrd" + item_state = "sheetrd" + +/obj/item/weapon/bedsheet/doublehos + icon_state = "doublesheethos" + item_state = "sheethos" + +/obj/item/weapon/bedsheet/doublehop + icon_state = "doublesheethop" + item_state = "sheethop" + +/obj/item/weapon/bedsheet/doublece + icon_state = "doublesheetce" + item_state = "sheetce" + +/obj/item/weapon/bedsheet/doublebrown + icon_state = "doublesheetbrown" + item_state = "sheetbrown" + +/obj/item/weapon/bedsheet/doubleian + icon_state = "doublesheetian" + item_state = "bedsheet" + /obj/structure/bedsheetbin name = "linen bin" diff --git a/code/game/objects/structures/stool_bed_chair_nest/bed.dm b/code/game/objects/structures/stool_bed_chair_nest/bed.dm index 73914e113c..511709200c 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/bed.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/bed.dm @@ -176,6 +176,19 @@ /obj/structure/bed/alien/New(var/newloc) ..(newloc,"resin") +/obj/structure/bed/double + name = "double bed" + icon_state = "doublebed" + base_icon = "doublebed" + +/obj/structure/bed/double/post_buckle_mob(mob/living/M as mob) + if(M == buckled_mob) + M.pixel_y = 13 + M.old_y = 13 + else + M.pixel_y = 0 + M.old_y = 0 + /* * Roller beds */ diff --git a/code/game/objects/structures/stool_bed_chair_nest/stools.dm b/code/game/objects/structures/stool_bed_chair_nest/stools.dm index e4840bedc6..e4d54f48ba 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/stools.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/stools.dm @@ -72,6 +72,11 @@ var/global/list/stool_cache = list() //haha stool /obj/item/weapon/stool/attack(mob/M as mob, mob/user as mob) if (prob(5) && istype(M,/mob/living)) user.visible_message("[user] breaks [src] over [M]'s back!") + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.do_attack_animation(M) + + user.drop_from_inventory(src) + user.remove_from_mob(src) dismantle() qdel(src) diff --git a/code/game/objects/structures/under_wardrobe.dm b/code/game/objects/structures/under_wardrobe.dm index a02c67c968..6a06ef26fb 100644 --- a/code/game/objects/structures/under_wardrobe.dm +++ b/code/game/objects/structures/under_wardrobe.dm @@ -5,39 +5,83 @@ icon_state = "cabinet_closed" density = 1 -/obj/structure/undies_wardrobe/attack_hand(mob/user as mob) - src.add_fingerprint(user) - var/mob/living/carbon/human/H = user - if(!ishuman(user) || (H.species && !(H.species.appearance_flags & HAS_UNDERWEAR))) +/obj/structure/undies_wardrobe/attack_hand(var/mob/user) + if(!human_who_can_use_underwear(user)) user << "Sadly there's nothing in here for you to wear." - return 0 + return + interact(user) - var/utype = alert("Which section do you want to pick from?",,"Underwear", "Undershirts", "Socks",) - var/list/selection - switch(utype) - if("Underwear") - utype = alert("Which section do you want to pick from?",, "Top", "Bottom",) - switch(utype) - if("Top") - selection = underwear_top_t - if("Bottom") - selection = underwear_bottom_t - if("Undershirts") - selection = undershirt_t - if("Socks") - selection = socks_t - var/pick = input("Select the style") as null|anything in selection - if(pick) - if(get_dist(src,user) > 1) +/obj/structure/undies_wardrobe/interact(var/mob/living/carbon/human/H) + var/dat = list() + dat += "Underwear:
" + for(var/datum/category_group/underwear/UWC in global_underwear.categories) + var/datum/category_item/underwear/UWI = H.all_underwear[UWC.name] + var/item_name = UWI ? UWI.name : "None" + dat += "[UWC.name]: [item_name]" + if(UWI) + for(var/datum/gear_tweak/gt in UWI.tweaks) + dat += " [gt.get_contents(get_metadata(H, UWC.name, gt))]" + dat += " (Remove)
" + + dat = jointext(dat,null) + H << browse(dat, "window=wardrobe;size=400x200") + +/obj/structure/undies_wardrobe/proc/get_metadata(var/mob/living/carbon/human/H, var/underwear_category, var/datum/gear_tweak/gt) + var/metadata = H.all_underwear_metadata[underwear_category] + if(!metadata) + metadata = list() + H.all_underwear_metadata[underwear_category] = metadata + + var/tweak_data = metadata["[gt]"] + if(!tweak_data) + tweak_data = gt.get_default() + metadata["[gt]"] = tweak_data + return tweak_data + +/obj/structure/undies_wardrobe/proc/set_metadata(var/mob/living/carbon/human/H, var/underwear_category, var/datum/gear_tweak/gt, var/new_metadata) + var/list/metadata = H.all_underwear_metadata[underwear_category] + metadata["[gt]"] = new_metadata + +/obj/structure/undies_wardrobe/proc/human_who_can_use_underwear(var/mob/living/carbon/human/H) + if(!istype(H) || !H.species || !(H.species.appearance_flags & HAS_UNDERWEAR)) + return FALSE + return TRUE + +/obj/structure/undies_wardrobe/CanUseTopic(var/user) + if(!human_who_can_use_underwear(user)) + return STATUS_CLOSE + + return ..() + +/obj/structure/undies_wardrobe/Topic(href, href_list, state) + if(..()) + return TRUE + + var/mob/living/carbon/human/H = usr + if(href_list["remove_underwear"]) + if(href_list["remove_underwear"] in H.all_underwear) + H.all_underwear -= href_list["remove_underwear"] + . = TRUE + else if(href_list["change_underwear"]) + var/datum/category_group/underwear/UWC = global_underwear.categories_by_name[href_list["change_underwear"]] + if(!UWC) return - if(utype == "Undershirts") - H.undershirt = selection[pick] - else if(utype == "Socks") - H.socks = selection[pick] - else if(utype == "Top") - H.underwear_top = selection[pick] - else - H.underwear_bottom = selection[pick] - H.update_body(1) + var/datum/category_item/underwear/selected_underwear = input(H, "Choose underwear:", "Choose underwear", H.all_underwear[UWC.name]) as null|anything in UWC.items + if(selected_underwear && CanUseTopic(H, default_state)) + H.all_underwear[UWC.name] = selected_underwear + . = TRUE + else if(href_list["underwear"] && href_list["tweak"]) + var/underwear = href_list["underwear"] + if(!(underwear in H.all_underwear)) + return + var/datum/gear_tweak/gt = locate(href_list["tweak"]) + if(!gt) + return + var/new_metadata = gt.get_metadata(usr, get_metadata(H, underwear, gt), "Wardrobe Underwear Selection") + if(new_metadata) + set_metadata(H, underwear, gt, new_metadata) + . = TRUE - return 1 \ No newline at end of file + if(.) + H.update_underwear() + interact(H) \ No newline at end of file diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 3c39c382fa..8e6c2cd0d5 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -271,6 +271,9 @@ else new glasstype(loc) qdel(src) + else if(istype(W,/obj/item/frame) && anchored) + var/obj/item/frame/F = W + F.try_build(src) else user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) if(W.damtype == BRUTE || W.damtype == BURN) diff --git a/code/game/turfs/flooring/flooring.dm b/code/game/turfs/flooring/flooring.dm index ad7a0f91ce..163867d1fa 100644 --- a/code/game/turfs/flooring/flooring.dm +++ b/code/game/turfs/flooring/flooring.dm @@ -68,7 +68,7 @@ var/list/flooring_types /decl/flooring/carpet/blue name = "carpet" icon_base = "bcarpet" - build_type = null + build_type = /obj/item/stack/tile/carpet/blue flags = TURF_HAS_EDGES | TURF_REMOVE_CROWBAR /decl/flooring/tiling diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index db48305926..9492cb87cf 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -164,6 +164,8 @@ material.place_dismantled_girder(src) if(!devastated) material.place_dismantled_product(src) + if (!reinf_material) + material.place_dismantled_product(src) for(var/obj/O in src.contents) //Eject contents! if(istype(O,/obj/structure/sign/poster)) diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm index fd9a37935e..3e6ed27302 100644 --- a/code/game/verbs/ooc.dm +++ b/code/game/verbs/ooc.dm @@ -29,8 +29,6 @@ if(prefs.muted & MUTE_OOC) src << "You cannot use OOC (muted)." return - if(handle_spam_prevention(msg,MUTE_OOC)) - return if(findtext(msg, "byond://")) src << "Advertising other servers is not allowed." log_admin("[key_name(src)] has attempted to advertise in OOC: [msg]") @@ -99,8 +97,6 @@ if(prefs.muted & MUTE_OOC) src << "You cannot use OOC (muted)." return - if(handle_spam_prevention(msg, MUTE_OOC)) - return if(findtext(msg, "byond://")) src << "Advertising other servers is not allowed." log_admin("[key_name(src)] has attempted to advertise in OOC: [msg]") @@ -140,7 +136,7 @@ send = 1 prefix = "(Eye) " - if(!send && (target in admins)) + if(!send && (target in admins) && target.is_preference_enabled(/datum/client_preference/holder/show_rlooc)) send = 1 prefix = "(R)" diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 23c5961b66..e045c19f1e 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -594,26 +594,33 @@ proc/admin_notice(var/message, var/rights) usr << browse(dat, "window=admin2;size=210x280") return -/datum/admins/proc/Secrets() +/datum/admins/proc/Secrets(var/datum/admin_secret_category/active_category = null) if(!check_rights(0)) return + // Print the header with category selection buttons. var/dat = "The first rule of adminbuse is: you don't talk about the adminbuse.
" for(var/datum/admin_secret_category/category in admin_secrets.categories) if(!category.can_view(usr)) continue - dat += "[category.name]
" - if(category.desc) - dat += "[category.desc]
" - for(var/datum/admin_secret_item/item in category.items) + dat += "[category.name] " + dat += "
" + + // If a category is selected, print its description and then options + if(istype(active_category) && active_category.can_view(usr)) + dat += "[active_category.name]
" + if(active_category.desc) + dat += "[active_category.desc]
" + for(var/datum/admin_secret_item/item in active_category.items) if(!item.can_view(usr)) continue dat += "[item.name()]
" dat += "
" - usr << browse(dat, "window=secrets") + + var/datum/browser/popup = new(usr, "secrets", "Secrets", 500, 500) + popup.set_content(dat) + popup.open() return - - /////////////////////////////////////////////////////////////////////////////////////////////////admins2.dm merge //i.e. buttons/verbs @@ -910,7 +917,7 @@ proc/admin_notice(var/message, var/rights) for(var/datum/antagonist/antag in ticker.mode.antag_templates) if(antag.is_antagonist(M)) return 2 - else if(M.special_role) + if(M.special_role) return 1 if(isrobot(character)) diff --git a/code/modules/admin/admin_secrets.dm b/code/modules/admin/admin_secrets.dm index 38bebb844a..b6d0abbe1c 100644 --- a/code/modules/admin/admin_secrets.dm +++ b/code/modules/admin/admin_secrets.dm @@ -22,6 +22,9 @@ var/datum/admin_secrets/admin_secrets = new() dd_insertObjectList(category.items, item) items += item +// +// Secret Item Category - Each subtype is a category for organizing secret commands. +// /datum/admin_secret_category var/name = "" var/desc = "" @@ -37,6 +40,10 @@ var/datum/admin_secrets/admin_secrets = new() return 1 return 0 +// +// Secret Item Datum - Each subtype is a command on the secrets panel. +// Override execute() with the implementation of the command. +// /datum/admin_secret_item var/name = "" var/category = null diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 78aca1da78..f049424b02 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -183,6 +183,7 @@ var/list/admin_verbs_debug = list( /client/proc/overlay_random_map, /client/proc/delete_random_map, /client/proc/show_plant_genes, + /client/proc/show_xenobio_genes, /client/proc/enable_debug_verbs, /client/proc/callproc, /client/proc/callproc_target, diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 99b3f7a2f4..cf98ede72f 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1644,6 +1644,10 @@ log_and_message_admins("created [number] [english_list(paths)]") return + else if(href_list["admin_secrets_panel"]) + var/datum/admin_secret_category/AC = locate(href_list["admin_secrets_panel"]) in admin_secrets.categories + src.Secrets(AC) + else if(href_list["admin_secrets"]) var/datum/admin_secret_item/item = locate(href_list["admin_secrets"]) in admin_secrets.items item.execute(usr) diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 504a8419bf..8f8532590e 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -17,9 +17,6 @@ var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey"," adminhelped = 1 //Determines if they get the message to reply by clicking the name. - if(src.handle_spam_prevention(msg,MUTE_ADMINHELP)) - return - //clean the input msg if(!msg) return diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index 898cc4e835..bcb87f06d8 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -56,9 +56,6 @@ else src << "Error: Private-Message: Client not found. They may have lost connection, so try using an adminhelp!" return - if (src.handle_spam_prevention(msg,MUTE_ADMINHELP)) - return - //clean the message if it's not sent by a high-rank admin //todo: sanitize for all??? if(!check_rights(R_SERVER|R_DEBUG,0)) diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index 11193a3f09..08eda6bb34 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -15,9 +15,6 @@ src << "You have deadchat muted." return - if (src.handle_spam_prevention(msg,MUTE_DEADCHAT)) - return - var/stafftype = uppertext(holder.rank) msg = sanitize(msg) diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm index 600549cdfa..23f5a75e2c 100644 --- a/code/modules/admin/verbs/pray.dm +++ b/code/modules/admin/verbs/pray.dm @@ -13,8 +13,6 @@ if(usr.client.prefs.muted & MUTE_PRAY) usr << "\red You cannot pray (muted)." return - if(src.client.handle_spam_prevention(msg,MUTE_PRAY)) - return var/image/cross = image('icons/obj/storage.dmi',"bible") msg = "\blue \icon[cross] PRAY: [key_name(src, 1)] (?) (PP) (VV) (SM) ([admin_jump_link(src, src)]) (CA) (SC): [msg]" diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index 5d8db9c072..170f9302f9 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -234,9 +234,9 @@ Ccomp's first proc. var/list/mobs = list() var/list/ghosts = list() - var/list/sortmob = sortAtom(mob_list) // get the mob list. + var/list/sortmob = sortAtom(mob_list) // get the mob list. var/any=0 - for(var/mob/observer/dead/M in sortmob) + for(var/mob/observer/dead/M in sortmob) mobs.Add(M) //filter it where it's only ghosts any = 1 //if no ghosts show up, any will just be 0 if(!any) @@ -388,7 +388,7 @@ Traitors and the like can also be revived with the previous role mostly intact. else new_character.gender = pick(MALE,FEMALE) var/datum/preferences/A = new() - A.randomize_appearance_for(new_character) + A.randomize_appearance_and_body_for(new_character) new_character.real_name = G_found.real_name if(!new_character.real_name) diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 643483bb79..336231e530 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -74,21 +74,6 @@ ..() //redirect to hsrc.Topic() -/client/proc/handle_spam_prevention(var/message, var/mute_type) - if(config.automute_on && !holder && src.last_message == message) - src.last_message_count++ - if(src.last_message_count >= SPAM_TRIGGER_AUTOMUTE) - src << "\red You have exceeded the spam filter limit for identical messages. An auto-mute was applied." - cmd_admin_mute(src.mob, mute_type, 1) - return 1 - if(src.last_message_count >= SPAM_TRIGGER_WARNING) - src << "\red You are nearing the spam filter limit for identical messages." - return 0 - else - last_message = message - src.last_message_count = 0 - return 0 - //This stops files larger than UPLOAD_LIMIT being sent from client to server via input(), client.Import() etc. /client/AllowUpload(filename, filelength) if(filelength > UPLOAD_LIMIT) diff --git a/code/modules/client/preference_setup/general/01_basic.dm b/code/modules/client/preference_setup/general/01_basic.dm index 2588836df5..656f54d477 100644 --- a/code/modules/client/preference_setup/general/01_basic.dm +++ b/code/modules/client/preference_setup/general/01_basic.dm @@ -80,7 +80,7 @@ datum/preferences/proc/set_biological_gender(var/gender) var/new_gender = input(user, "Choose your character's biological gender:", "Character Preference", pref.biological_gender) as null|anything in get_genders() if(new_gender && CanUseTopic(user)) pref.set_biological_gender(new_gender) - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["id_gender"]) var/new_gender = input(user, "Choose your character's identifying gender:", "Character Preference", pref.identifying_gender) as null|anything in all_genders_define_list diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm index ee93b2acf4..a092b859b3 100644 --- a/code/modules/client/preference_setup/general/03_body.dm +++ b/code/modules/client/preference_setup/general/03_body.dm @@ -1,5 +1,8 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-") +/datum/preferences + var/dress_mob = TRUE + /datum/category_item/player_setup_item/general/body name = "Body" sort_order = 3 @@ -25,6 +28,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O S["disabilities"] >> pref.disabilities S["organ_data"] >> pref.organ_data S["rlimb_data"] >> pref.rlimb_data + pref.preview_icon = null /datum/category_item/player_setup_item/general/body/save_character(var/savefile/S) S["species"] << pref.species @@ -74,10 +78,9 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O /datum/category_item/player_setup_item/general/body/content(var/mob/user) . = list() - pref.update_preview_icon() - if(pref.preview_icon_front && pref.preview_icon_side) - user << browse_rsc(pref.preview_icon_front, "preview_icon.png") - user << browse_rsc(pref.preview_icon_side, "preview_icon2.png") + if(!pref.preview_icon) + pref.update_preview_icon() + user << browse_rsc(pref.preview_icon, "previewicon.png") var/mob_species = all_species[pref.species] . += "
Body " @@ -167,7 +170,10 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O else . += "

" - . += "
Preview
" + . += "
Preview
" + . += "
" + . += "
[pref.dress_mob ? "Hide equipment" : "Show equipment"]" + . += "
" . += "Hair
" @@ -196,8 +202,8 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O var/datum/species/mob_species = all_species[pref.species] if(href_list["random"]) - pref.randomize_appearance_for() - return TOPIC_REFRESH + pref.randomize_appearance_and_body_for() + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["blood_type"]) var/new_b_type = input(user, "Choose your character's blood-type:", "Character Preference") as null|anything in valid_bloodtypes @@ -274,7 +280,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O var/datum/species/S = all_species[pref.species] pref.age = max(min(pref.age, S.max_age), S.min_age) - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["hair_color"]) if(!has_flag(mob_species, HAS_HAIR_COLOR)) @@ -284,7 +290,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O pref.r_hair = hex2num(copytext(new_hair, 2, 4)) pref.g_hair = hex2num(copytext(new_hair, 4, 6)) pref.b_hair = hex2num(copytext(new_hair, 6, 8)) - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["hair_style"]) var/list/valid_hairstyles = list() @@ -298,7 +304,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O var/new_h_style = input(user, "Choose your character's hair style:", "Character Preference", pref.h_style) as null|anything in valid_hairstyles if(new_h_style && CanUseTopic(user)) pref.h_style = new_h_style - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["facial_color"]) if(!has_flag(mob_species, HAS_HAIR_COLOR)) @@ -308,7 +314,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O pref.r_facial = hex2num(copytext(new_facial, 2, 4)) pref.g_facial = hex2num(copytext(new_facial, 4, 6)) pref.b_facial = hex2num(copytext(new_facial, 6, 8)) - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["eye_color"]) if(!has_flag(mob_species, HAS_EYE_COLOR)) @@ -318,7 +324,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O pref.r_eyes = hex2num(copytext(new_eyes, 2, 4)) pref.g_eyes = hex2num(copytext(new_eyes, 4, 6)) pref.b_eyes = hex2num(copytext(new_eyes, 6, 8)) - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["skin_tone"]) if(!has_flag(mob_species, HAS_SKIN_TONE)) @@ -326,7 +332,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O var/new_s_tone = input(user, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Character Preference", (-pref.s_tone) + 35) as num|null if(new_s_tone && has_flag(mob_species, HAS_SKIN_TONE) && CanUseTopic(user)) pref.s_tone = 35 - max(min( round(new_s_tone), 220),1) - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["skin_color"]) if(!has_flag(mob_species, HAS_SKIN_COLOR)) @@ -336,7 +342,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O pref.r_skin = hex2num(copytext(new_skin, 2, 4)) pref.g_skin = hex2num(copytext(new_skin, 4, 6)) pref.b_skin = hex2num(copytext(new_skin, 6, 8)) - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["facial_style"]) var/list/valid_facialhairstyles = list() @@ -354,11 +360,11 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O var/new_f_style = input(user, "Choose your character's facial-hair style:", "Character Preference", pref.f_style) as null|anything in valid_facialhairstyles if(new_f_style && has_flag(mob_species, HAS_HAIR_COLOR) && CanUseTopic(user)) pref.f_style = new_f_style - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["reset_limbs"]) reset_limbs() - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["limbs"]) @@ -419,7 +425,6 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O switch(new_state) if("Normal") - if(limb == BP_TORSO) for(var/other_limb in BP_ALL - BP_TORSO) pref.organ_data[other_limb] = null @@ -444,6 +449,8 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O var/list/usable_manufacturers = list() for(var/company in chargen_robolimbs) var/datum/robolimb/M = chargen_robolimbs[company] + if(!(limb in M.parts)) + continue if(tmp_species in M.species_cannot_use) continue usable_manufacturers[company] = M @@ -473,7 +480,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O for(var/internal_organ in list(O_HEART,O_EYES)) pref.organ_data[internal_organ] = "mechanical" - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["organs"]) @@ -511,7 +518,11 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O else if(href_list["disabilities"]) var/disability_flag = text2num(href_list["disabilities"]) pref.disabilities ^= disability_flag - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["toggle_clothing"]) + pref.dress_mob = !pref.dress_mob + return TOPIC_REFRESH_UPDATE_PREVIEW return ..() diff --git a/code/modules/client/preference_setup/general/04_equipment.dm b/code/modules/client/preference_setup/general/04_equipment.dm index e356551507..c455cef524 100644 --- a/code/modules/client/preference_setup/general/04_equipment.dm +++ b/code/modules/client/preference_setup/general/04_equipment.dm @@ -1,90 +1,119 @@ +/datum/preferences + var/list/all_underwear + var/list/all_underwear_metadata + /datum/category_item/player_setup_item/general/equipment name = "Clothing" sort_order = 4 /datum/category_item/player_setup_item/general/equipment/load_character(var/savefile/S) - S["underwear_top"] >> pref.underwear_top - S["underwear_bottom"] >> pref.underwear_bottom - S["undershirt"] >> pref.undershirt - S["socks"] >> pref.socks + S["all_underwear"] >> pref.all_underwear + S["all_underwear_metadata"] >> pref.all_underwear_metadata S["backbag"] >> pref.backbag S["pdachoice"] >> pref.pdachoice /datum/category_item/player_setup_item/general/equipment/save_character(var/savefile/S) - S["underwear_top"] << pref.underwear_top - S["underwear_bottom"] << pref.underwear_bottom - S["undershirt"] << pref.undershirt - S["socks"] << pref.socks + S["all_underwear"] << pref.all_underwear + S["all_underwear_metadata"] << pref.all_underwear_metadata S["backbag"] << pref.backbag S["pdachoice"] << pref.pdachoice /datum/category_item/player_setup_item/general/equipment/sanitize_character() + if(!islist(pref.gear)) pref.gear = list() + + if(!istype(pref.all_underwear)) + pref.all_underwear = list() + + for(var/datum/category_group/underwear/WRC in global_underwear.categories) + for(var/datum/category_item/underwear/WRI in WRC.items) + if(WRI.is_default(pref.identifying_gender ? pref.identifying_gender : MALE)) + pref.all_underwear[WRC.name] = WRI.name + break + + if(!istype(pref.all_underwear_metadata)) + pref.all_underwear_metadata = list() + + for(var/underwear_category in pref.all_underwear) + var/datum/category_group/underwear/UWC = global_underwear.categories_by_name[underwear_category] + if(!UWC) + pref.all_underwear -= underwear_category + else + var/datum/category_item/underwear/UWI = UWC.items_by_name[pref.all_underwear[underwear_category]] + if(!UWI) + pref.all_underwear -= underwear_category + + for(var/underwear_metadata in pref.all_underwear_metadata) + if(!(underwear_metadata in pref.all_underwear)) + pref.all_underwear_metadata -= underwear_metadata pref.backbag = sanitize_integer(pref.backbag, 1, backbaglist.len, initial(pref.backbag)) pref.pdachoice = sanitize_integer(pref.pdachoice, 1, pdachoicelist.len, initial(pref.pdachoice)) - if(!islist(pref.gear)) pref.gear = list() - - var/undies_top = get_undies_top() - var/undies_bottom = get_undies_bottom() - if(!get_key_by_value(undies_top, pref.underwear_top)) - pref.underwear_top = undies_top[1] - if(!get_key_by_value(undies_bottom, pref.underwear_bottom)) - pref.underwear_bottom = undies_bottom[1] - if(!get_key_by_value(undershirt_t, pref.undershirt)) - pref.undershirt = undershirt_t[1] - if(!get_key_by_value(socks_t, pref.socks)) - pref.socks = socks_t[1] - /datum/category_item/player_setup_item/general/equipment/content() + . = list() . += "Equipment:
" - . += "Underwear Top: [get_key_by_value(get_undies_top(),pref.underwear_top)]
" - . += "Underwear Bottom: [get_key_by_value(get_undies_bottom(),pref.underwear_bottom)]
" - . += "Undershirt: [get_key_by_value(undershirt_t,pref.undershirt)]
" - . += "Socks: [get_key_by_value(socks_t,pref.socks)]
" + for(var/datum/category_group/underwear/UWC in global_underwear.categories) + var/item_name = pref.all_underwear[UWC.name] ? pref.all_underwear[UWC.name] : "None" + . += "[UWC.name]: [item_name]" + var/datum/category_item/underwear/UWI = UWC.items_by_name[item_name] + if(UWI) + for(var/datum/gear_tweak/gt in UWI.tweaks) + . += " [gt.get_contents(get_metadata(UWC.name, gt))]" + + . += "
" . += "Backpack Type: [backbaglist[pref.backbag]]
" . += "PDA Type: [pdachoicelist[pref.pdachoice]]
" -/datum/category_item/player_setup_item/general/equipment/proc/get_undies_top() - return underwear_top_t -/datum/category_item/player_setup_item/general/equipment/proc/get_undies_bottom() - return underwear_bottom_t + return jointext(.,null) + +/datum/category_item/player_setup_item/general/equipment/proc/get_metadata(var/underwear_category, var/datum/gear_tweak/gt) + var/metadata = pref.all_underwear_metadata[underwear_category] + if(!metadata) + metadata = list() + pref.all_underwear_metadata[underwear_category] = metadata + + var/tweak_data = metadata["[gt]"] + if(!tweak_data) + tweak_data = gt.get_default() + metadata["[gt]"] = tweak_data + return tweak_data + +/datum/category_item/player_setup_item/general/equipment/proc/set_metadata(var/underwear_category, var/datum/gear_tweak/gt, var/new_metadata) + var/list/metadata = pref.all_underwear_metadata[underwear_category] + metadata["[gt]"] = new_metadata + /datum/category_item/player_setup_item/general/equipment/OnTopic(var/href,var/list/href_list, var/mob/user) - if(href_list["change_underwear_top"]) - var/underwear_top_options = get_undies_top() - var/new_underwear_top = input(user, "Choose your character's top underwear:", "Character Preference", get_key_by_value(get_undies_top(),pref.underwear_top)) as null|anything in underwear_top_options - if(!isnull(new_underwear_top) && CanUseTopic(user)) - pref.underwear_top = underwear_top_options[new_underwear_top] - return TOPIC_REFRESH - - else if(href_list["change_underwear_bottom"]) - var/underwear_bottom_options = get_undies_bottom() - var/new_underwear_bottom = input(user, "Choose your character's bottom underwear:", "Character Preference", get_key_by_value(get_undies_bottom(),pref.underwear_bottom)) as null|anything in underwear_bottom_options - if(!isnull(new_underwear_bottom) && CanUseTopic(user)) - pref.underwear_bottom = underwear_bottom_options[new_underwear_bottom] - return TOPIC_REFRESH - - else if(href_list["change_undershirt"]) - var/new_undershirt = input(user, "Choose your character's undershirt:", "Character Preference", get_key_by_value(undershirt_t,pref.undershirt)) as null|anything in undershirt_t - if(!isnull(new_undershirt) && CanUseTopic(user)) - pref.undershirt = undershirt_t[new_undershirt] - return TOPIC_REFRESH - - else if(href_list["change_socks"]) - var/new_socks = input(user, "Choose your character's socks:", "Character Preference", get_key_by_value(socks_t,pref.socks)) as null|anything in socks_t - if(!isnull(new_socks) && CanUseTopic(user)) - pref.socks = socks_t[new_socks] - return TOPIC_REFRESH - - else if(href_list["change_backpack"]) + if(href_list["change_backpack"]) var/new_backbag = input(user, "Choose your character's style of bag:", "Character Preference", backbaglist[pref.backbag]) as null|anything in backbaglist if(!isnull(new_backbag) && CanUseTopic(user)) pref.backbag = backbaglist.Find(new_backbag) - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["change_pda"]) var/new_pdachoice = input(user, "Choose your character's style of PDA:", "Character Preference", pdachoicelist[pref.pdachoice]) as null|anything in pdachoicelist if(!isnull(new_pdachoice) && CanUseTopic(user)) pref.pdachoice = pdachoicelist.Find(new_pdachoice) return TOPIC_REFRESH - return ..() + + else if(href_list["change_underwear"]) + var/datum/category_group/underwear/UWC = global_underwear.categories_by_name[href_list["change_underwear"]] + if(!UWC) + return + var/datum/category_item/underwear/selected_underwear = input(user, "Choose underwear:", "Character Preference", pref.all_underwear[UWC.name]) as null|anything in UWC.items + if(selected_underwear && CanUseTopic(user)) + pref.all_underwear[UWC.name] = selected_underwear.name + return TOPIC_REFRESH_UPDATE_PREVIEW + + else if(href_list["underwear"] && href_list["tweak"]) + var/underwear = href_list["underwear"] + if(!(underwear in pref.all_underwear)) + return TOPIC_NOACTION + var/datum/gear_tweak/gt = locate(href_list["tweak"]) + if(!gt) + return TOPIC_NOACTION + var/new_metadata = gt.get_metadata(usr, get_metadata(underwear, gt)) + if(new_metadata) + set_metadata(underwear, gt, new_metadata) + return TOPIC_REFRESH_UPDATE_PREVIEW + + return ..() \ No newline at end of file diff --git a/code/modules/client/preference_setup/global/setting_datums.dm b/code/modules/client/preference_setup/global/setting_datums.dm index e5d408c477..e0e0356a6c 100644 --- a/code/modules/client/preference_setup/global/setting_datums.dm +++ b/code/modules/client/preference_setup/global/setting_datums.dm @@ -137,6 +137,13 @@ var/list/_client_preferences_by_type key = "SAFE_FIRING" enabled_description = "Safe" disabled_description = "Dangerous" + +/datum/client_preference/browser_style + description = "Fake NanoUI Browser Style" + key = "BROWSER_STYLED" + enabled_description = "Fancy" + disabled_description = "Plain" + /******************** * Staff Preferences * ********************/ @@ -182,4 +189,10 @@ var/list/_client_preferences_by_type description = "Radio chatter" key = "CHAT_RADIO" enabled_description = "Show" - disabled_description = "Hide" \ No newline at end of file + disabled_description = "Hide" + +/datum/client_preference/holder/show_rlooc + description ="Remote LOOC chat" + key = "CHAT_RLOOC" + enabled_description = "Show" + disabled_description = "Hide" diff --git a/code/modules/client/preference_setup/loadout/gear_tweaks.dm b/code/modules/client/preference_setup/loadout/gear_tweaks.dm index 820c95f324..e8b1a56d62 100644 --- a/code/modules/client/preference_setup/loadout/gear_tweaks.dm +++ b/code/modules/client/preference_setup/loadout/gear_tweaks.dm @@ -17,8 +17,6 @@ * Color adjustment */ -var/datum/gear_tweak/color/gear_tweak_free_color_choice = new() - /datum/gear_tweak/color var/list/valid_colors @@ -32,10 +30,10 @@ var/datum/gear_tweak/color/gear_tweak_free_color_choice = new() /datum/gear_tweak/color/get_default() return valid_colors ? valid_colors[1] : COLOR_GRAY -/datum/gear_tweak/color/get_metadata(var/user, var/metadata) +/datum/gear_tweak/color/get_metadata(var/user, var/metadata, var/title = "Character Preference") if(valid_colors) - return input(user, "Choose an item color.", "Character Preference", metadata) as null|anything in valid_colors - return input(user, "Choose an item color.", "Global Preference", metadata) as color|null + return input(user, "Choose a color.", title, metadata) as null|anything in valid_colors + return input(user, "Choose a color.", title, metadata) as color|null /datum/gear_tweak/color/tweak_item(var/obj/item/I, var/metadata) if(valid_colors && !(metadata in valid_colors)) diff --git a/code/modules/client/preference_setup/loadout/loadout.dm b/code/modules/client/preference_setup/loadout/loadout.dm index e2d73ff8b3..dd494b03bc 100644 --- a/code/modules/client/preference_setup/loadout/loadout.dm +++ b/code/modules/client/preference_setup/loadout/loadout.dm @@ -63,6 +63,8 @@ var/list/gear_datums = list() return valid_gear_choices /datum/category_item/player_setup_item/loadout/sanitize_character() + if(!islist(pref.gear)) + pref.gear = list() for(var/gear_name in pref.gear) if(!(gear_name in gear_datums)) @@ -169,7 +171,7 @@ var/list/gear_datums = list() if(istype(G)) total_cost += G.cost if((total_cost+TG.cost) <= MAX_GEAR_COST) pref.gear += TG.display_name - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW if(href_list["gear"] && href_list["tweak"]) var/datum/gear/gear = gear_datums[href_list["gear"]] var/datum/gear_tweak/tweak = locate(href_list["tweak"]) @@ -179,13 +181,13 @@ var/list/gear_datums = list() if(!metadata || !CanUseTopic(user)) return TOPIC_NOACTION set_tweak_metadata(gear, tweak, metadata) - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["select_category"]) current_tab = href_list["select_category"] return TOPIC_REFRESH else if(href_list["clear_loadout"]) pref.gear.Cut() - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW return ..() /datum/gear diff --git a/code/modules/client/preference_setup/loadout/loadout_accessories.dm b/code/modules/client/preference_setup/loadout/loadout_accessories.dm index a5c66d2e79..a90373d219 100644 --- a/code/modules/client/preference_setup/loadout/loadout_accessories.dm +++ b/code/modules/client/preference_setup/loadout/loadout_accessories.dm @@ -53,10 +53,34 @@ display_name = "tie, blue" path = /obj/item/clothing/accessory/blue +/datum/gear/accessory/tie/blue_clip + display_name = "tie, blue with clip" + path = /obj/item/clothing/accessory/blue_clip + +/datum/gear/accessory/tie/blue_long + display_name = "tie, blue long" + path = /obj/item/clothing/accessory/blue_long + /datum/gear/accessory/tie/red display_name = "tie, red" path = /obj/item/clothing/accessory/red +/datum/gear/accessory/tie/red_clip + display_name = "tie, red with clip" + path = /obj/item/clothing/accessory/red_clip + +/datum/gear/accessory/tie/red_long + display_name = "tie, red long" + path = /obj/item/clothing/accessory/red_long + +/datum/gear/accessory/tie/yellow + display_name = "tie, yellow" + path = /obj/item/clothing/accessory/yellow + +/datum/gear/accessory/tie/navy + display_name = "tie, navy blue" + path = /obj/item/clothing/accessory/navy + /datum/gear/accessory/tie/horrible display_name = "tie, socially disgraceful" path = /obj/item/clothing/accessory/horrible @@ -121,6 +145,30 @@ display_name = "scarf, striped blue" path = /obj/item/clothing/accessory/stripedbluescarf +/datum/gear/accessory/suitjacket + display_name = "suit jacket, tan" + path = /obj/item/clothing/accessory/tan_jacket + +/datum/gear/accessory/suitjacket/charcoal + display_name = "suit jacket, charcoal" + path = /obj/item/clothing/accessory/charcoal_jacket + +/datum/gear/accessory/suitjacket/navy + display_name = "suit jacket, navy blue" + path = /obj/item/clothing/accessory/navy_jacket + +/datum/gear/accessory/suitjacket/burgundy + display_name = "suit jacket, burgundy" + path = /obj/item/clothing/accessory/burgundy_jacket + +/datum/gear/accessory/suitjacket/checkered + display_name = "suit jacket, checkered" + path = /obj/item/clothing/accessory/checkered_jacket + +/datum/gear/accessory/suitvest + display_name = "suit vest" + path = /obj/item/clothing/accessory/vest + /datum/gear/accessory/brown_vest display_name = "webbing, engineering" path = /obj/item/clothing/accessory/storage/brown_vest diff --git a/code/modules/client/preference_setup/loadout/loadout_general.dm b/code/modules/client/preference_setup/loadout/loadout_general.dm index 2b1c4bbb6b..b659f8db13 100644 --- a/code/modules/client/preference_setup/loadout/loadout_general.dm +++ b/code/modules/client/preference_setup/loadout/loadout_general.dm @@ -61,3 +61,21 @@ lunchboxes[initial(lunchbox.name)] = lunchbox_type gear_tweaks += new/datum/gear_tweak/path(sortAssoc(lunchboxes)) gear_tweaks += new/datum/gear_tweak/contents(lunchables_lunches(), lunchables_snacks(), lunchables_drinks()) + +/datum/gear/towel + display_name = "towel" + path = /obj/item/weapon/towel + +/datum/gear/towel/New() + ..() + gear_tweaks = list(gear_tweak_free_color_choice) + +/datum/gear/cahwhite + display_name = "Cards Against The Galaxy (white deck)" + path = /obj/item/weapon/deck/cah + description = "The ever-popular Cards Against The Galaxy word game. Warning: may include traces of broken fourth wall. This is the white deck." + +/datum/gear/cahblack + display_name = "Cards Against The Galaxy (black deck)" + path = /obj/item/weapon/deck/cah/black + description = "The ever-popular Cards Against The Galaxy word game. Warning: may include traces of broken fourth wall. This is the black deck." diff --git a/code/modules/client/preference_setup/loadout/loadout_shoes.dm b/code/modules/client/preference_setup/loadout/loadout_shoes.dm index 80912e2581..d6e61c561e 100644 --- a/code/modules/client/preference_setup/loadout/loadout_shoes.dm +++ b/code/modules/client/preference_setup/loadout/loadout_shoes.dm @@ -7,7 +7,7 @@ /datum/gear/shoes/toeless display_name = "toe-less jackboots" - path = /obj/item/clothing/shoes/jackboots/unathi + path = /obj/item/clothing/shoes/jackboots/toeless /datum/gear/shoes/workboots display_name = "workboots" diff --git a/code/modules/client/preference_setup/loadout/loadout_suit.dm b/code/modules/client/preference_setup/loadout/loadout_suit.dm index a86f0e214f..40bd1d767b 100644 --- a/code/modules/client/preference_setup/loadout/loadout_suit.dm +++ b/code/modules/client/preference_setup/loadout/loadout_suit.dm @@ -143,7 +143,7 @@ /datum/gear/suit/roles/poncho/science display_name = "poncho, science" path = /obj/item/clothing/suit/poncho/roles/science - allowed_roles = list("Research Director","Scientist", "Roboticist", "Xenobotanist") + allowed_roles = list("Research Director","Scientist", "Roboticist", "Xenobiologist") /datum/gear/suit/roles/poncho/cargo display_name = "poncho, cargo" @@ -218,7 +218,7 @@ /datum/gear/suit/wintercoat/science display_name = "winter coat, science" path = /obj/item/clothing/suit/storage/hooded/wintercoat/science - allowed_roles = list("Research Director","Scientist", "Roboticist", "Xenobotanist") + allowed_roles = list("Research Director","Scientist", "Roboticist", "Xenobiologist") /datum/gear/suit/wintercoat/engineering display_name = "winter coat, engineering" @@ -233,7 +233,7 @@ /datum/gear/suit/wintercoat/hydro display_name = "winter coat, hydroponics" path = /obj/item/clothing/suit/storage/hooded/wintercoat/hydro - allowed_roles = list("Botanist", "Xenobotanist") + allowed_roles = list("Botanist", "Xenobiologist") /datum/gear/suit/wintercoat/cargo display_name = "winter coat, cargo" @@ -255,4 +255,16 @@ for(var/varsity_style in typesof(/obj/item/clothing/suit/varsity)) var/obj/item/clothing/suit/varsity/varsity = varsity_style varsities[initial(varsity.name)] = varsity - gear_tweaks += new/datum/gear_tweak/path(sortAssoc(varsities)) \ No newline at end of file + gear_tweaks += new/datum/gear_tweak/path(sortAssoc(varsities)) + +/datum/gear/suit/track + display_name = "track jacket selection" + path = /obj/item/clothing/suit/storage/toggle/track + +/datum/gear/suit/track/New() + ..() + var/list/tracks = list() + for(var/track_style in typesof(/obj/item/clothing/suit/storage/toggle/track)) + var/obj/item/clothing/suit/storage/toggle/track/track = track_style + tracks[initial(track.name)] = track + gear_tweaks += new/datum/gear_tweak/path(sortAssoc(tracks)) \ No newline at end of file diff --git a/code/modules/client/preference_setup/loadout/loadout_uniform.dm b/code/modules/client/preference_setup/loadout/loadout_uniform.dm index 1ef69de38a..a6665d0bb5 100644 --- a/code/modules/client/preference_setup/loadout/loadout_uniform.dm +++ b/code/modules/client/preference_setup/loadout/loadout_uniform.dm @@ -88,72 +88,72 @@ shorts[initial(short_type.name)] = short_type gear_tweaks += new/datum/gear_tweak/path(sortAssoc(shorts)) -/datum/gear/uniform/skirt/ce +/datum/gear/uniform/job_skirt/ce display_name = "skirt, ce" path = /obj/item/clothing/under/rank/engineer/chief_engineer/skirt allowed_roles = list("Chief Engineer") -/datum/gear/uniform/skirt/atmos +/datum/gear/uniform/job_skirt/atmos display_name = "skirt, atmos" path = /obj/item/clothing/under/rank/engineer/atmospheric_technician/skirt allowed_roles = list("Chief Engineer","Atmospheric Technician") -/datum/gear/uniform/skirt/eng +/datum/gear/uniform/job_skirt/eng display_name = "skirt, engineer" path = /obj/item/clothing/under/rank/engineer/skirt allowed_roles = list("Chief Engineer","Station Engineer") -/datum/gear/uniform/skirt/roboticist +/datum/gear/uniform/job_skirt/roboticist display_name = "skirt, roboticist" path = /obj/item/clothing/under/rank/roboticist/skirt allowed_roles = list("Research Director","Roboticist") -/datum/gear/uniform/skirt/cmo +/datum/gear/uniform/job_skirt/cmo display_name = "skirt, cmo" path = /obj/item/clothing/under/rank/medical/chief_medical_officer/skirt allowed_roles = list("Chief Medical Officer") -/datum/gear/uniform/skirt/chem +/datum/gear/uniform/job_skirt/chem display_name = "skirt, chemist" path = /obj/item/clothing/under/rank/medical/chemist/skirt allowed_roles = list("Chief Medical Officer","Chemist") -/datum/gear/uniform/skirt/viro +/datum/gear/uniform/job_skirt/viro display_name = "skirt, virologist" path = /obj/item/clothing/under/rank/medical/virologist/skirt allowed_roles = list("Chief Medical Officer","Medical Doctor") -/datum/gear/uniform/skirt/med +/datum/gear/uniform/job_skirt/med display_name = "skirt, medical" path = /obj/item/clothing/under/rank/medical/skirt allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic") -/datum/gear/uniform/skirt/sci +/datum/gear/uniform/job_skirt/sci display_name = "skirt, scientist" path = /obj/item/clothing/under/rank/scientist/skirt - allowed_roles = list("Research Director","Scientist") + allowed_roles = list("Research Director","Scientist", "Xenobiologist") -/datum/gear/uniform/skirt/cargo +/datum/gear/uniform/job_skirt/cargo display_name = "skirt, cargo" path = /obj/item/clothing/under/rank/cargotech/skirt allowed_roles = list("Quartermaster","Cargo Technician") -/datum/gear/uniform/skirt/qm +/datum/gear/uniform/job_skirt/qm display_name = "skirt, QM" path = /obj/item/clothing/under/rank/cargo/skirt allowed_roles = list("Quartermaster") -/datum/gear/uniform/skirt/warden +/datum/gear/uniform/job_skirt/warden display_name = "skirt, warden" path = /obj/item/clothing/under/rank/security/warden/skirt allowed_roles = list("Head of Security", "Warden") -/datum/gear/uniform/skirt/security +/datum/gear/uniform/job_skirt/security display_name = "skirt, security" path = /obj/item/clothing/under/rank/security/skirt allowed_roles = list("Head of Security", "Warden", "Detective", "Security Officer") -/datum/gear/uniform/skirt/head_of_security +/datum/gear/uniform/job_skirt/head_of_security display_name = "skirt, hos" path = /obj/item/clothing/under/rank/security/head_of_security/skirt allowed_roles = list("Head of Security") diff --git a/code/modules/client/preference_setup/occupation/occupation.dm b/code/modules/client/preference_setup/occupation/occupation.dm index 09dd7f573e..b3704bfb0c 100644 --- a/code/modules/client/preference_setup/occupation/occupation.dm +++ b/code/modules/client/preference_setup/occupation/occupation.dm @@ -160,10 +160,10 @@ var/choice = input("Choose an title for [job.title].", "Choose Title", pref.GetPlayerAltTitle(job)) as anything in choices|null if(choice && CanUseTopic(user)) SetPlayerAltTitle(job, choice) - return TOPIC_REFRESH + return (pref.dress_mob ? TOPIC_REFRESH_UPDATE_PREVIEW : TOPIC_REFRESH) else if(href_list["set_job"]) - if(SetJob(user, href_list["set_job"])) return TOPIC_REFRESH + if(SetJob(user, href_list["set_job"])) return (pref.dress_mob ? TOPIC_REFRESH_UPDATE_PREVIEW : TOPIC_REFRESH) return ..() diff --git a/code/modules/client/preference_setup/preference_setup.dm b/code/modules/client/preference_setup/preference_setup.dm index a890db16f5..7028894fda 100644 --- a/code/modules/client/preference_setup/preference_setup.dm +++ b/code/modules/client/preference_setup/preference_setup.dm @@ -1,7 +1,8 @@ -// These are not flags, binary operations not intended #define TOPIC_NOACTION 0 #define TOPIC_HANDLED 1 #define TOPIC_REFRESH 2 +#define TOPIC_UPDATE_PREVIEW 4 +#define TOPIC_REFRESH_UPDATE_PREVIEW (TOPIC_REFRESH|TOPIC_UPDATE_PREVIEW) /datum/category_group/player_setup_category/general_preferences name = "General" @@ -217,7 +218,9 @@ return 1 . = OnTopic(href, href_list, usr) - if(. == TOPIC_REFRESH) + if(. & TOPIC_UPDATE_PREVIEW) + pref_mob.client.prefs.preview_icon = null + if(. & TOPIC_REFRESH) pref_mob.client.prefs.ShowChoices(usr) /datum/category_item/player_setup_item/CanUseTopic(var/mob/user) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 0e9bf5323b..665ea5836c 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -28,10 +28,6 @@ datum/preferences var/age = 30 //age of character var/spawnpoint = "Arrivals Shuttle" //where this character will spawn (0-2). var/b_type = "A+" //blood type (not-chooseable) - var/underwear_top - var/underwear_bottom - var/undershirt //undershirt type - var/socks //socks type var/backbag = 2 //backpack type var/pdachoice = 1 //PDA type var/h_style = "Bald" //Hair type @@ -63,8 +59,6 @@ datum/preferences //Mob preview var/icon/preview_icon = null - var/icon/preview_icon_front = null - var/icon/preview_icon_side = null //Jobs, uses bitflags var/job_civilian_high = 0 @@ -122,7 +116,7 @@ datum/preferences player_setup = new(src) set_biological_gender(pick(MALE, FEMALE)) real_name = random_name(identifying_gender,species) - b_type = pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+") + b_type = RANDOM_BLOOD_TYPE gear = list() @@ -213,7 +207,7 @@ datum/preferences dat += "" //user << browse(dat, "window=preferences;size=635x736") - var/datum/browser/popup = new(user, "Character Setup","Character Setup", 635, 736, src) + var/datum/browser/popup = new(user, "Character Setup","Character Setup", 800, 800, src) popup.set_content(dat) popup.open() @@ -254,9 +248,10 @@ datum/preferences ShowChoices(usr) return 1 -/datum/preferences/proc/copy_to(mob/living/carbon/human/character, safety = 0) +/datum/preferences/proc/copy_to(mob/living/carbon/human/character, icon_updates = 1) // Sanitizing rather than saving as someone might still be editing when copy_to occurs. player_setup.sanitize_setup() + character.set_species(species) if(be_random_name) real_name = random_name(identifying_gender,species) @@ -297,10 +292,12 @@ datum/preferences character.g_eyes = g_eyes character.b_eyes = b_eyes + character.h_style = h_style character.r_hair = r_hair character.g_hair = g_hair character.b_hair = b_hair + character.f_style = f_style character.r_facial = r_facial character.g_facial = g_facial character.b_facial = b_facial @@ -346,10 +343,18 @@ datum/preferences else if(status == "mechanical") I.robotize() - character.underwear_bottom = underwear_bottom - character.underwear_top = underwear_top - character.undershirt = undershirt - character.socks = socks + character.all_underwear.Cut() + character.all_underwear_metadata.Cut() + + for(var/underwear_category_name in all_underwear) + var/datum/category_group/underwear/underwear_category = global_underwear.categories_by_name[underwear_category_name] + if(underwear_category) + var/underwear_item_name = all_underwear[underwear_category_name] + character.all_underwear[underwear_category_name] = underwear_category.items_by_name[underwear_item_name] + if(all_underwear_metadata[underwear_category_name]) + character.all_underwear_metadata[underwear_category_name] = all_underwear_metadata[underwear_category_name] + else + all_underwear -= underwear_category_name if(backbag > 4 || backbag < 1) backbag = 1 //Same as above @@ -359,7 +364,13 @@ datum/preferences pdachoice = 1 character.pdachoice = pdachoice - character.update_body() + if(icon_updates) + character.force_update_limbs() + character.update_mutations(0) + character.update_body(0) + character.update_underwear(0) + character.update_hair(0) + character.update_icons() /datum/preferences/proc/open_load_dialog(mob/user) var/dat = "" diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 8c6fd55c69..87b6b7540d 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -4,6 +4,11 @@ var/list/species_restricted = null //Only these species can wear this kit. var/gunshot_residue //Used by forensics. + var/list/accessories = list() + var/list/valid_accessory_slots + var/list/restricted_accessory_slots + var/list/starting_accessories + /* Sprites used when the clothing item is refit. This is done by setting icon_override. For best results, if this is set then sprite_sheets should be null and vice versa, but that is by no means necessary. @@ -22,6 +27,13 @@ ..() gunshot_residue = null +/obj/item/clothing/New() + ..() + if(starting_accessories) + for(var/T in starting_accessories) + var/obj/item/clothing/accessory/tie = new T(src) + src.attach_accessory(null, tie) + //BS12: Species-restricted clothing check. /obj/item/clothing/mob_can_equip(M as mob, slot) @@ -501,7 +513,6 @@ 2 = Report detailed damages 3 = Report location */ - var/list/accessories = list() var/displays_id = 1 var/rolled_down = -1 //0 = unrolled, 1 = rolled, -1 = cannot be toggled var/rolled_sleeves = -1 //0 = unrolled, 1 = rolled, -1 = cannot be toggled @@ -512,6 +523,16 @@ //convenience var for defining the icon state for the overlay used when the clothing is worn. //Also used by rolling/unrolling. var/worn_state = null + valid_accessory_slots = list("utility","armband","decor") + restricted_accessory_slots = list("utility", "armband") + + +/obj/item/clothing/under/attack_hand(var/mob/user) + if(accessories && accessories.len) + ..() + if ((ishuman(usr) || issmall(usr)) && src.loc == user) + return + ..() /obj/item/clothing/under/New() ..() @@ -578,69 +599,6 @@ var/mob/M = src.loc M.update_inv_w_uniform() -/obj/item/clothing/under/proc/can_attach_accessory(obj/item/clothing/accessory/A) - if(istype(A)) - .=1 - else - return 0 - if(accessories.len && (A.slot in list("utility","armband"))) - for(var/obj/item/clothing/accessory/AC in accessories) - if (AC.slot == A.slot) - return 0 - -/obj/item/clothing/under/attackby(obj/item/I, mob/user) - if(istype(I, /obj/item/clothing/accessory)) - var/obj/item/clothing/accessory/A = I - if(can_attach_accessory(A)) - user.drop_item() - accessories += A - A.on_attached(src, user) - - if(istype(loc, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = loc - H.update_inv_w_uniform() - - return - else - user << "You cannot attach more accessories of this type to [src]." - - if(accessories.len) - for(var/obj/item/clothing/accessory/A in accessories) - A.attackby(I, user) - return - - ..() - -/obj/item/clothing/under/attack_hand(mob/user as mob) - //only forward to the attached accessory if the clothing is equipped (not in a storage) - if(accessories.len && src.loc == user) - for(var/obj/item/clothing/accessory/A in accessories) - A.attack_hand(user) - return - - if ((ishuman(usr) || issmall(usr)) && src.loc == user) //make it harder to accidentally undress yourself - return - - ..() - -/obj/item/clothing/under/MouseDrop(obj/over_object as obj) - if (ishuman(usr) || issmall(usr)) - //makes sure that the clothing is equipped so that we can't drag it into our hand from miles away. - if (!(src.loc == usr)) - return - - if (( usr.restrained() ) || ( usr.stat )) - return - - if (!usr.unEquip(src)) - return - - switch(over_object.name) - if("r_hand") - usr.put_in_r_hand(src) - if("l_hand") - usr.put_in_l_hand(src) - src.add_fingerprint(usr) /obj/item/clothing/under/examine(mob/user) ..(user) @@ -653,9 +611,6 @@ user << "Its vital tracker appears to be enabled." if(3) user << "Its vital tracker and tracking beacon appear to be enabled." - if(accessories.len) - for(var/obj/item/clothing/accessory/A in accessories) - user << "\A [A] is attached to it." /obj/item/clothing/under/proc/set_sensors(mob/usr as mob) var/mob/M = usr @@ -678,27 +633,16 @@ if (src.loc == usr) switch(sensor_mode) if(0) - usr << "You disable your suit's remote sensing equipment." + usr.visible_message("[usr] adjusts their sensors.", "You disable your suit's remote sensing equipment.") if(1) - usr << "Your suit will now report whether you are live or dead." + usr.visible_message("[usr] adjusts their sensors.", "Your suit will now report whether you are live or dead.") if(2) - usr << "Your suit will now report your vital lifesigns." + usr.visible_message("[usr] adjusts their sensors.", "Your suit will now report your vital lifesigns.") if(3) - usr << "Your suit will now report your vital lifesigns as well as your coordinate position." + usr.visible_message("[usr] adjusts their sensors.", "Your suit will now report your vital lifesigns as well as your coordinate position.") + else if (istype(src.loc, /mob)) - switch(sensor_mode) - if(0) - for(var/mob/V in viewers(usr, 1)) - V.show_message("\red [usr] disables [src.loc]'s remote sensing equipment.", 1) - if(1) - for(var/mob/V in viewers(usr, 1)) - V.show_message("[usr] turns [src.loc]'s remote sensors to binary.", 1) - if(2) - for(var/mob/V in viewers(usr, 1)) - V.show_message("[usr] sets [src.loc]'s sensors to track vitals.", 1) - if(3) - for(var/mob/V in viewers(usr, 1)) - V.show_message("[usr] sets [src.loc]'s sensors to maximum.", 1) + usr.visible_message("[usr] adjusts [src.loc]'s sensors.", "You adjust [src.loc]'s sensors.") /obj/item/clothing/under/verb/toggle() set name = "Toggle Suit Sensors" @@ -759,34 +703,7 @@ usr << "You roll down your [src]'s sleeves." update_clothing_icon() -/obj/item/clothing/under/proc/remove_accessory(mob/user, obj/item/clothing/accessory/A) - if(!(A in accessories)) - return - - A.on_removed(user) - accessories -= A - update_clothing_icon() - -/obj/item/clothing/under/verb/removetie() - set name = "Remove Accessory" - set category = "Object" - set src in usr - if(!istype(usr, /mob/living)) return - if(usr.stat) return - if(!accessories.len) return - var/obj/item/clothing/accessory/A - if(accessories.len > 1) - A = input("Select an accessory to remove from [src]") as null|anything in accessories - else - A = accessories[1] - src.remove_accessory(usr,A) /obj/item/clothing/under/rank/New() sensor_mode = pick(0,1,2,3) ..() - -/obj/item/clothing/under/emp_act(severity) - if(accessories.len) - for(var/obj/item/clothing/accessory/A in accessories) - A.emp_act(severity) - ..() \ No newline at end of file diff --git a/code/modules/clothing/clothing_accessories.dm b/code/modules/clothing/clothing_accessories.dm new file mode 100644 index 0000000000..b0b3d217ed --- /dev/null +++ b/code/modules/clothing/clothing_accessories.dm @@ -0,0 +1,107 @@ +/obj/item/clothing/proc/can_attach_accessory(obj/item/clothing/accessory/A) + if(valid_accessory_slots && istype(A) && (A.slot in valid_accessory_slots)) + .=1 + else + return 0 + if(accessories.len && restricted_accessory_slots && (A.slot in restricted_accessory_slots)) + for(var/obj/item/clothing/accessory/AC in accessories) + if (AC.slot == A.slot) + return 0 + +/obj/item/clothing/attackby(var/obj/item/I, var/mob/user) + if(istype(I, /obj/item/clothing/accessory)) + + if(!valid_accessory_slots || !valid_accessory_slots.len) + usr << "You cannot attach accessories of any kind to \the [src]." + return + + var/obj/item/clothing/accessory/A = I + if(can_attach_accessory(A)) + user.drop_item() + attach_accessory(user, A) + return + else + user << "You cannot attach more accessories of this type to [src]." + return + + if(accessories.len) + for(var/obj/item/clothing/accessory/A in accessories) + A.attackby(I, user) + return + + ..() + +/obj/item/clothing/attack_hand(var/mob/user) + //only forward to the attached accessory if the clothing is equipped (not in a storage) + if(accessories.len && src.loc == user) + for(var/obj/item/clothing/accessory/A in accessories) + A.attack_hand(user) + return + return ..() + +/obj/item/clothing/MouseDrop(var/obj/over_object) + if (ishuman(usr) || issmall(usr)) + //makes sure that the clothing is equipped so that we can't drag it into our hand from miles away. + if (!(src.loc == usr)) + return + + if (( usr.restrained() ) || ( usr.stat )) + return + + if (!usr.unEquip(src)) + return + + switch(over_object.name) + if("r_hand") + usr.put_in_r_hand(src) + if("l_hand") + usr.put_in_l_hand(src) + src.add_fingerprint(usr) + +/obj/item/clothing/examine(var/mob/user) + ..(user) + if(accessories.len) + for(var/obj/item/clothing/accessory/A in accessories) + user << "\A [A] is attached to it." + +/** + * Attach accessory A to src + * + * user is the user doing the attaching. Can be null, such as when attaching + * items on spawn + */ +/obj/item/clothing/proc/attach_accessory(mob/user, obj/item/clothing/accessory/A) + accessories += A + A.on_attached(src, user) + src.verbs |= /obj/item/clothing/proc/removetie_verb + update_clothing_icon() + +/obj/item/clothing/proc/remove_accessory(mob/user, obj/item/clothing/accessory/A) + if(!(A in accessories)) + return + + A.on_removed(user) + accessories -= A + update_clothing_icon() + +/obj/item/clothing/proc/removetie_verb() + set name = "Remove Accessory" + set category = "Object" + set src in usr + if(!istype(usr, /mob/living)) return + if(usr.stat) return + if(!accessories.len) return + var/obj/item/clothing/accessory/A + if(accessories.len > 1) + A = input("Select an accessory to remove from [src]") as null|anything in accessories + else + A = accessories[1] + src.remove_accessory(usr,A) + if(!accessories.len) + src.verbs -= /obj/item/clothing/proc/removetie_verb + +/obj/item/clothing/emp_act(severity) + if(accessories.len) + for(var/obj/item/clothing/accessory/A in accessories) + A.emp_act(severity) + ..() \ No newline at end of file diff --git a/code/modules/clothing/masks/monitor.dm b/code/modules/clothing/masks/monitor.dm index b2bfc13876..9d0651d5ba 100644 --- a/code/modules/clothing/masks/monitor.dm +++ b/code/modules/clothing/masks/monitor.dm @@ -9,28 +9,10 @@ icon = 'icons/mob/monitor_icons.dmi' icon_override = 'icons/mob/monitor_icons.dmi' - icon_state = "ipc_blank" - item_state = null + icon_state = "monitor" var/monitor_state_index = "blank" - var/global/list/monitor_states = list( - "blank" = "ipc_blank", - "pink" = "ipc_pink", - "red" = "ipc_red", - "green" = "ipc_green", - "blue" = "ipc_blue", - "breakout" = "ipc_breakout", - "eight" = "ipc_eight", - "goggles" = "ipc_goggles", - "heart" = "ipc_heart", - "monoeye" = "ipc_monoeye", - "nature" = "ipc_nature", - "orange" = "ipc_orange", - "purple" = "ipc_purple", - "shower" = "ipc_shower", - "static" = "ipc_static", - "yellow" = "ipc_yellow" - ) + var/global/list/monitor_states = list() /obj/item/clothing/mask/monitor/set_dir() dir = SOUTH @@ -40,8 +22,13 @@ ..() var/mob/living/carbon/human/H = loc if(istype(H) && H.wear_mask == src) + var/obj/item/organ/external/E = H.organs_by_name[BP_HEAD] + var/datum/robolimb/robohead = all_robolimbs[E.model] canremove = 0 - H << "\The [src] connects to your display output." + if(robohead.monitor_styles) + monitor_states = params2list(robohead.monitor_styles) + icon_state = monitor_states[monitor_state_index] + H << "\The [src] connects to your display output." /obj/item/clothing/mask/monitor/dropped() canremove = 1 @@ -52,9 +39,10 @@ return 0 if(istype(user)) var/obj/item/organ/external/E = user.organs_by_name[BP_HEAD] - if(istype(E) && (E.status & ORGAN_ROBOT)) + var/datum/robolimb/robohead = all_robolimbs[E.model] + if(istype(E) && (E.robotic >= ORGAN_ROBOT) && robohead.monitor_styles) return 1 - user << "You must have a robotic head to install this upgrade." + user << "You must have a compatible robotic head to install this upgrade." return 0 /obj/item/clothing/mask/monitor/verb/set_monitor_state() diff --git a/code/modules/clothing/shoes/jobs.dm b/code/modules/clothing/shoes/jobs.dm index 9e3bc0e2cf..768595c2ae 100644 --- a/code/modules/clothing/shoes/jobs.dm +++ b/code/modules/clothing/shoes/jobs.dm @@ -17,7 +17,7 @@ siemens_coefficient = 0.7 can_hold_knife = 1 -/obj/item/clothing/shoes/jackboots/unathi +/obj/item/clothing/shoes/jackboots/toeless name = "toe-less jackboots" desc = "Modified pair of jackboots, particularly friendly to those species whose toes hold claws." item_state = "digiboots" diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index 26d7de9082..4d90082708 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -133,7 +133,6 @@ chest = new chest_type(src) if(allowed) chest.allowed = allowed - chest.slowdown = offline_slowdown verbs |= /obj/item/weapon/rig/proc/toggle_chest for(var/obj/item/piece in list(gloves,helmet,boots,chest)) @@ -332,14 +331,14 @@ offline = 0 if(istype(wearer) && !wearer.wearing_rig) wearer.wearing_rig = src - chest.slowdown = initial(slowdown) + slowdown = initial(slowdown) if(offline) if(offline == 1) for(var/obj/item/rig_module/module in installed_modules) module.deactivate() offline = 2 - chest.slowdown = offline_slowdown + slowdown = offline_slowdown return if(cell && cell.charge > 0 && electrified > 0) diff --git a/code/modules/clothing/spacesuits/rig/suits/alien.dm b/code/modules/clothing/spacesuits/rig/suits/alien.dm index e2ded4c468..5662e8b181 100644 --- a/code/modules/clothing/spacesuits/rig/suits/alien.dm +++ b/code/modules/clothing/spacesuits/rig/suits/alien.dm @@ -1,7 +1,7 @@ -/obj/item/weapon/rig/unathi - name = "NT breacher chassis control module" +/obj/item/weapon/rig/breacher + name = "\improper NT breacher chassis control module" desc = "A cheap NT knock-off of an Unathi battle-rig. Looks like a fish, moves like a fish, steers like a cow." - suit_type = "NT breacher" + suit_type = "\improper NT breacher" icon_state = "breacher_rig_cheap" armor = list(melee = 60, bullet = 60, laser = 60, energy = 60, bomb = 70, bio = 100, rad = 50) emp_protection = -20 @@ -10,11 +10,11 @@ vision_restriction = 1 offline_vision_restriction = 2 - chest_type = /obj/item/clothing/suit/space/rig - helm_type = /obj/item/clothing/head/helmet/space/rig/unathi - boot_type = /obj/item/clothing/shoes/magboots/rig/unathi + chest_type = /obj/item/clothing/suit/space/rig/breacher + helm_type = /obj/item/clothing/head/helmet/space/rig/breacher + boot_type = /obj/item/clothing/shoes/magboots/rig/breacher -/obj/item/weapon/rig/unathi/fancy +/obj/item/weapon/rig/breacher/fancy name = "breacher chassis control module" desc = "An authentic Unathi breacher chassis. Huge, bulky and absurdly heavy. It must be like wearing a tank." suit_type = "breacher chassis" @@ -22,12 +22,12 @@ armor = list(melee = 90, bullet = 90, laser = 90, energy = 90, bomb = 90, bio = 100, rad = 80) //Takes TEN TIMES as much damage to stop someone in a breacher. In exchange, it's slow. vision_restriction = 0 -/obj/item/clothing/head/helmet/space/rig/unathi +/obj/item/clothing/head/helmet/space/rig/breacher species_restricted = list("Unathi") force = 5 -/obj/item/clothing/suit/space/rig/unathi +/obj/item/clothing/suit/space/rig/breacher species_restricted = list("Unathi") -/obj/item/clothing/shoes/magboots/rig/unathi +/obj/item/clothing/shoes/magboots/rig/breacher species_restricted = list("Unathi") \ No newline at end of file diff --git a/code/modules/clothing/spacesuits/void/void.dm b/code/modules/clothing/spacesuits/void/void.dm index 6632e9a86b..c0d9e58a2c 100644 --- a/code/modules/clothing/spacesuits/void/void.dm +++ b/code/modules/clothing/spacesuits/void/void.dm @@ -210,8 +210,11 @@ if(!istype(user,/mob/living)) return + if(istype(W,/obj/item/clothing/accessory) || istype(W, /obj/item/weapon/hand_labeler)) + return ..() + if(istype(src.loc,/mob/living)) - user << "How do you propose to modify a voidsuit while it is being worn?" + user << "You cannot modify \the [src] while it is being worn." return if(istype(W,/obj/item/weapon/screwdriver)) diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index bfcde568e9..b856f8a8bb 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -46,14 +46,14 @@ desc = "An armoured jacket with silver rank pips and livery." icon_state = "warden_jacket" item_state = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS /obj/item/clothing/suit/armor/vest/warden/alt name = "Warden's jacket" desc = "An armoured jacket with silver rank pips and livery." icon_state = "warden_alt" item_state = "warden_alt" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS /obj/item/clothing/suit/armor/riot diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index ed80f4dd61..413685df45 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -681,4 +681,42 @@ /obj/item/clothing/suit/varsity/brown name = "brown varsity jacket" - icon_state = "varsity_brown" \ No newline at end of file + icon_state = "varsity_brown" + +//Track Jackets + +/obj/item/clothing/suit/storage/toggle/track + name = "track jacket" + desc = "a track jacket, for the athletic." + icon_state = "trackjacket" + item_state = "trackjacket" + icon_open = "trackjacket_open" + icon_closed = "trackjacket" + +/obj/item/clothing/suit/storage/toggle/track/blue + name = "blue track jacket" + icon_state = "trackjacketblue" + item_state = "trackjacketblue" + icon_open = "trackjacketblue_open" + icon_closed = "trackjacketblue" + +/obj/item/clothing/suit/storage/toggle/track/green + name = "green track jacket" + icon_state = "trackjacketgreen" + item_state = "trackjacketgreen" + icon_open = "trackjacketgreen_open" + icon_closed = "trackjacketgreen" + +/obj/item/clothing/suit/storage/toggle/track/red + name = "red track jacket" + icon_state = "trackjacketred" + item_state = "trackjacketred" + icon_open = "trackjacketred_open" + icon_closed = "trackjacketred" + +/obj/item/clothing/suit/storage/toggle/track/white + name = "white track jacket" + icon_state = "trackjacketwhite" + item_state = "trackjacketwhite" + icon_open = "trackjacketwhite_open" + icon_closed = "trackjacketwhite" \ No newline at end of file diff --git a/code/modules/clothing/under/accessories/accessory.dm b/code/modules/clothing/under/accessories/accessory.dm index 3725fccd30..c9ac0fb4f4 100644 --- a/code/modules/clothing/under/accessories/accessory.dm +++ b/code/modules/clothing/under/accessories/accessory.dm @@ -7,11 +7,15 @@ slot_flags = SLOT_TIE w_class = 2.0 var/slot = "decor" - var/obj/item/clothing/under/has_suit = null //the suit the tie may be attached to + var/obj/item/clothing/has_suit = null //the suit the tie may be attached to var/image/inv_overlay = null //overlay used when attached to clothing. var/image/mob_overlay = null var/overlay_state = null +/obj/item/clothing/accessory/Destroy() + on_removed() + return ..() + /obj/item/clothing/accessory/proc/get_inv_overlay() if(!inv_overlay) if(!mob_overlay) @@ -36,23 +40,27 @@ return mob_overlay //when user attached an accessory to S -/obj/item/clothing/accessory/proc/on_attached(obj/item/clothing/under/S, mob/user as mob) +/obj/item/clothing/accessory/proc/on_attached(var/obj/item/clothing/S, var/mob/user) if(!istype(S)) return has_suit = S loc = has_suit has_suit.overlays += get_inv_overlay() - user << "You attach [src] to [has_suit]." - src.add_fingerprint(user) + if(user) + user << "You attach \the [src] to \the [has_suit]." + src.add_fingerprint(user) -/obj/item/clothing/accessory/proc/on_removed(mob/user as mob) +/obj/item/clothing/accessory/proc/on_removed(var/mob/user) if(!has_suit) return has_suit.overlays -= get_inv_overlay() has_suit = null - usr.put_in_hands(src) - src.add_fingerprint(user) + if(user) + usr.put_in_hands(src) + src.add_fingerprint(user) + else + src.forceMove(get_turf(src)) //default attackby behaviour /obj/item/clothing/accessory/attackby(obj/item/I, mob/user) @@ -72,10 +80,34 @@ name = "red tie" icon_state = "redtie" +/obj/item/clothing/accessory/blue_clip + name = "blue tie with a clip" + icon_state = "bluecliptie" + +/obj/item/clothing/accessory/blue_long + name = "blue long tie" + icon_state = "bluelongtie" + +/obj/item/clothing/accessory/red_clip + name = "red tie with a clip" + icon_state = "redcliptie" + +/obj/item/clothing/accessory/red_long + name = "red long tie" + icon_state = "redlongtie" + /obj/item/clothing/accessory/black name = "black tie" icon_state = "blacktie" +/obj/item/clothing/accessory/yellow + name = "yellow tie" + icon_state = "yellowtie" + +/obj/item/clothing/accessory/navy + name = "navy tie" + icon_state = "navytie" + /obj/item/clothing/accessory/horrible name = "horrible tie" desc = "A neosilk clip-on tie. This one is disgusting." @@ -100,7 +132,7 @@ var/sound_strength = "cannot hear" var/heartbeat = 0 var/obj/item/organ/internal/heart/heart = M.internal_organs_by_name[O_HEART] - if(heart && !(heart.status & ORGAN_ROBOT)) + if(heart && !(heart.robotic >= ORGAN_ROBOT)) heartbeat = 1 if(M.stat == DEAD || (M.status_flags&FAKEDEATH)) sound_strength = "cannot hear" diff --git a/code/modules/clothing/under/accessories/badges.dm b/code/modules/clothing/under/accessories/badges.dm index b78b0ffac3..2908a7b639 100644 --- a/code/modules/clothing/under/accessories/badges.dm +++ b/code/modules/clothing/under/accessories/badges.dm @@ -38,6 +38,8 @@ /obj/item/clothing/accessory/badge/attack(mob/living/carbon/human/M, mob/living/user) if(isliving(user)) user.visible_message("[user] invades [M]'s personal space, thrusting [src] into their face insistently.","You invade [M]'s personal space, thrusting [src] into their face insistently.") + user.do_attack_animation(M) + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //to prevent spam //.Holobadges. /obj/item/clothing/accessory/badge/holo @@ -127,4 +129,4 @@ new /obj/item/clothing/accessory/badge/holo/hos(src) new /obj/item/clothing/accessory/badge/holo/cord(src) ..() - return \ No newline at end of file + return diff --git a/code/modules/clothing/under/accessories/clothing.dm b/code/modules/clothing/under/accessories/clothing.dm new file mode 100644 index 0000000000..59b2ba1797 --- /dev/null +++ b/code/modules/clothing/under/accessories/clothing.dm @@ -0,0 +1,29 @@ +/obj/item/clothing/accessory/vest + name = "black vest" + desc = "Slick black suit vest." + icon_state = "det_vest" + +/obj/item/clothing/accessory/tan_jacket + name = "tan suit jacket" + desc = "Cozy suit jacket." + icon_state = "tan_jacket" + +/obj/item/clothing/accessory/charcoal_jacket + name = "charcoal suit jacket" + desc = "Strict suit jacket." + icon_state = "charcoal_jacket" + +/obj/item/clothing/accessory/navy_jacket + name = "navy suit jacket" + desc = "Official suit jacket." + icon_state = "navy_jacket" + +/obj/item/clothing/accessory/burgundy_jacket + name = "burgundy suit jacket" + desc = "Expensive suit jacket." + icon_state = "burgundy_jacket" + +/obj/item/clothing/accessory/checkered_jacket + name = "checkered suit jacket" + desc = "Lucky suit jacket." + icon_state = "checkered_jacket" \ No newline at end of file diff --git a/code/modules/clothing/under/jobs/civilian.dm b/code/modules/clothing/under/jobs/civilian.dm index 30bf89c1c3..5f16483bb2 100644 --- a/code/modules/clothing/under/jobs/civilian.dm +++ b/code/modules/clothing/under/jobs/civilian.dm @@ -115,6 +115,7 @@ item_state = "ba_suit" worn_state = "internalaffairs" rolled_sleeves = 0 + starting_accessories = list(/obj/item/clothing/accessory/black) /obj/item/clothing/under/rank/janitor desc = "It's the official uniform of the station's janitor. It has minor protection from biohazards." @@ -155,10 +156,11 @@ /obj/item/clothing/under/lawyer/bluesuit name = "Blue Suit" - desc = "A classy suit and tie" + desc = "A classy suit." icon_state = "bluesuit" item_state = "ba_suit" worn_state = "bluesuit" + starting_accessories = list(/obj/item/clothing/accessory/red) /obj/item/clothing/under/lawyer/purpsuit name = "Purple Suit" diff --git a/code/modules/clothing/under/jobs/security.dm b/code/modules/clothing/under/jobs/security.dm index cdc6d4b3e4..564a962125 100644 --- a/code/modules/clothing/under/jobs/security.dm +++ b/code/modules/clothing/under/jobs/security.dm @@ -81,13 +81,14 @@ */ /obj/item/clothing/under/det name = "detective's suit" - desc = "A rumpled white dress shirt paired with well-worn grey slacks, complete with a blue striped tie and a faux-gold tie clip." + desc = "A rumpled white dress shirt paired with well-worn grey slacks." icon_state = "detective" item_state = "det" worn_state = "detective" armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) siemens_coefficient = 0.9 rolled_sleeves = 0 + starting_accessories = list(/obj/item/clothing/accessory/blue_clip) /* /obj/item/clothing/under/det/verb/rollup() @@ -103,13 +104,15 @@ /obj/item/clothing/under/det/grey icon_state = "detective2" worn_state = "detective2" - desc = "A serious-looking tan dress shirt paired with freshly-pressed black slacks, complete with a red striped tie." + desc = "A serious-looking tan dress shirt paired with freshly-pressed black slacks." + starting_accessories = list(/obj/item/clothing/accessory/red_long) /obj/item/clothing/under/det/black icon_state = "detective3" worn_state = "detective3" item_state = "sl_suit" desc = "An immaculate white dress shirt, paired with a pair of dark grey dress pants, a red tie, and a charcoal vest." + starting_accessories = list(/obj/item/clothing/accessory/red_long, /obj/item/clothing/accessory/vest) /obj/item/clothing/under/det/corporate name = "detective's jumpsuit" diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index 57f2849117..4753580272 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -488,6 +488,7 @@ icon_state = "charcoal_suit" item_state = "bl_suit" worn_state = "charcoal_suit" + starting_accessories = list(/obj/item/clothing/accessory/navy, /obj/item/clothing/accessory/charcoal_jacket) /obj/item/clothing/under/suit_jacket/navy name = "navy suit" @@ -495,6 +496,7 @@ icon_state = "navy_suit" item_state = "bl_suit" worn_state = "navy_suit" + starting_accessories = list(/obj/item/clothing/accessory/red, /obj/item/clothing/accessory/navy_jacket) /obj/item/clothing/under/suit_jacket/burgundy name = "burgundy suit" @@ -502,6 +504,7 @@ icon_state = "burgundy_suit" item_state = "r_suit" worn_state = "burgundy_suit" + starting_accessories = list(/obj/item/clothing/accessory/black, /obj/item/clothing/accessory/burgundy_jacket) /obj/item/clothing/under/suit_jacket/checkered name = "checkered suit" @@ -509,13 +512,15 @@ icon_state = "checkered_suit" item_state = "gy_suit" worn_state = "checkered_suit" + starting_accessories = list(/obj/item/clothing/accessory/black, /obj/item/clothing/accessory/checkered_jacket) /obj/item/clothing/under/suit_jacket/tan name = "tan suit" - desc = "A tan suit with a yellow tie. Smart, but casual." + desc = "A tan suit. Smart, but casual." icon_state = "tan_suit" item_state = "lb_suit" worn_state = "tan_suit" + starting_accessories = list(/obj/item/clothing/accessory/yellow, /obj/item/clothing/accessory/tan_jacket) /obj/item/clothing/under/serviceoveralls name = "workman outfit" diff --git a/code/modules/clothing/under/pants.dm b/code/modules/clothing/under/pants.dm index 9fdcb52b29..c0f3157c65 100644 --- a/code/modules/clothing/under/pants.dm +++ b/code/modules/clothing/under/pants.dm @@ -55,6 +55,22 @@ desc = "A pair of track pants, for the athletic." icon_state = "trackpants" +/obj/item/clothing/under/pants/track/blue + name = "blue track pants" + icon_state = "trackpantsblue" + +/obj/item/clothing/under/pants/track/green + name = "green track pants" + icon_state = "trackpantsgreen" + +/obj/item/clothing/under/pants/track/white + name = "white track pants" + icon_state = "trackpantswhite" + +/obj/item/clothing/under/pants/track/red + name = "red track pants" + icon_state = "trackpantsred" + /obj/item/clothing/under/pants/khaki name = "khaki pants" desc = "A pair of dust beige khaki pants." diff --git a/code/modules/clothing/under/shorts.dm b/code/modules/clothing/under/shorts.dm index c4b712c4c3..b04621bdfe 100644 --- a/code/modules/clothing/under/shorts.dm +++ b/code/modules/clothing/under/shorts.dm @@ -116,26 +116,31 @@ name = "khaki skirt" desc = "A skirt that is a khaki color." icon_state = "skirt_khaki" + worn_state = "skirt_khaki" /obj/item/clothing/under/skirt/black name = "short black skirt" desc = "A skirt that is a shiny black." icon_state = "skirt_short_black" + worn_state = "skirt_short_black" /obj/item/clothing/under/skirt/blue name = "short blue skirt" desc = "A skirt that is a shiny blue." icon_state = "skirt_short_blue" + worn_state = "skirt_short_blue" /obj/item/clothing/under/skirt/red name = "short red skirt" desc = "A skirt that is a shiny red." icon_state = "skirt_short_red" + worn_state = "skirt_short_red" /obj/item/clothing/under/skirt/swept name = "swept skirt" desc = "A skirt that is swept to one side." icon_state = "skirt_swept" + worn_state = "skirt_swept" /obj/item/clothing/under/skirt/plaid_blue name = "blue plaid skirt" diff --git a/code/modules/detectivework/tools/rag.dm b/code/modules/detectivework/tools/rag.dm index 6887ef1fba..06ee34b312 100644 --- a/code/modules/detectivework/tools/rag.dm +++ b/code/modules/detectivework/tools/rag.dm @@ -49,7 +49,7 @@ if(!on_fire && istype(W, /obj/item/weapon/flame)) var/obj/item/weapon/flame/F = W if(F.lit) - ignite() + src.ignite() if(on_fire) visible_message("\The [user] lights [src] with [W].") else @@ -151,15 +151,21 @@ /obj/item/weapon/reagent_containers/glass/rag/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume) if(exposed_temperature >= 50 + T0C) - ignite() + src.ignite() if(exposed_temperature >= 900 + T0C) new /obj/effect/decal/cleanable/ash(get_turf(src)) qdel(src) -//rag must have a minimum of 2 units welder fuel and at least 80% of the reagents must be welder fuel. -//maybe generalize flammable reagents someday +//rag must have a minimum of 2 units welder fuel or ehtanol based reagents and at least 80% of the reagents must so. /obj/item/weapon/reagent_containers/glass/rag/proc/can_ignite() - var/fuel = reagents.get_reagent_amount("fuel") + var/fuel + if(reagents.get_reagent_amount("fuel")) + fuel += reagents.get_reagent_amount("fuel") + + else + for(var/datum/reagent/ethanol/R in reagents.reagent_list) + fuel += reagents.get_reagent_amount(R.id) + return (fuel >= 2 && fuel >= reagents.total_volume*0.8) /obj/item/weapon/reagent_containers/glass/rag/proc/ignite() @@ -217,5 +223,8 @@ return reagents.remove_reagent("fuel", reagents.maximum_volume/25) + for(var/datum/reagent/ethanol/R in reagents.reagent_list) + if(istype(R, /datum/reagent/ethanol)) + reagents.remove_reagent(R.id, reagents.maximum_volume/25) update_name() burn_time-- diff --git a/code/modules/flufftext/Dreaming.dm b/code/modules/flufftext/Dreaming.dm index 69032cd5ce..6ac9483e2f 100644 --- a/code/modules/flufftext/Dreaming.dm +++ b/code/modules/flufftext/Dreaming.dm @@ -11,7 +11,7 @@ var/list/dreams = list( "the quartermaster","a cargo technician","the botanist","a shaft miner","the psychologist","the chemist","the geneticist", "the virologist","the roboticist","the chef","the bartender","the chaplain","the librarian","a mouse","an ert member", "a beach","the holodeck","a smokey room","a voice","the cold","a mouse","an operating table","the bar","the rain","a skrell", - "a unathi","a tajaran","the ai core","the mining station","the research station","a beaker of strange liquid", + "an unathi","a tajaran","the ai core","the mining station","the research station","a beaker of strange liquid", ) mob/living/carbon/proc/dream() diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm index bc0f1a376c..f9536420e2 100644 --- a/code/modules/holodeck/HolodeckObjects.dm +++ b/code/modules/holodeck/HolodeckObjects.dm @@ -263,9 +263,6 @@ return 1 return 0 -/obj/item/weapon/holo/esword/attack(target as mob, mob/user as mob) - ..() - /obj/item/weapon/holo/esword/New() item_color = pick("red","blue","green","purple") diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 608398a039..1a89c9c0a4 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -236,66 +236,22 @@ return ..() -/obj/item/weapon/reagent_containers/food/snacks/grown/attack(var/mob/living/carbon/M, var/mob/user, var/def_zone) - if(user == M) - return ..() +/obj/item/weapon/reagent_containers/food/snacks/grown/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) + . = ..() - if(user.a_intent == I_HURT) - - // This is being copypasted here because reagent_containers (WHY DOES FOOD DESCEND FROM THAT) overrides it completely. - // TODO: refactor all food paths to be less horrible and difficult to work with in this respect. ~Z - if(!istype(M) || (can_operate(M) && do_surgery(M,user,src))) return 0 - - user.lastattacked = M - M.lastattacker = user - user.attack_log += "\[[time_stamp()]\] Attacked [M.name] ([M.ckey]) with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])" - M.attack_log += "\[[time_stamp()]\] Attacked by [user.name] ([user.ckey]) with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])" - msg_admin_attack("[key_name(user)] attacked [key_name(M)] with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])" ) - - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/hit = H.attacked_by(src, user, def_zone) - if(hit && hitsound) - playsound(loc, hitsound, 50, 1, -1) - return hit - else - if(attack_verb.len) - user.visible_message("[M] has been [pick(attack_verb)] with [src] by [user]!") - else - user.visible_message("[M] has been attacked with [src] by [user]!") - - if (hitsound) - playsound(loc, hitsound, 50, 1, -1) - switch(damtype) - if("brute") - M.take_organ_damage(force) - if(prob(33)) - var/turf/simulated/location = get_turf(M) - if(istype(location)) location.add_blood_floor(M) - if("fire") - if (!(COLD_RESISTANCE in M.mutations)) - M.take_organ_damage(0, force) - M.updatehealth() - - if(seed && seed.get_trait(TRAIT_STINGS)) - if(!reagents || reagents.total_volume <= 0) - return - reagents.remove_any(rand(1,3)) - seed.thrown_at(src,M) - sleep(-1) - if(!src) - return - if(prob(35)) - if(user) - user << "\The [src] has fallen to bits." - user.drop_from_inventory(src) - qdel(src) - - add_fingerprint(user) - return 1 - - else - ..() + if(seed && seed.get_trait(TRAIT_STINGS)) + if(!reagents || reagents.total_volume <= 0) + return + reagents.remove_any(rand(1,3)) + seed.thrown_at(src, target) + sleep(-1) + if(!src) + return + if(prob(35)) + if(user) + user << "\The [src] has fallen to bits." + user.drop_from_inventory(src) + qdel(src) /obj/item/weapon/reagent_containers/food/snacks/grown/attack_self(mob/user as mob) diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index 0974a705ce..9115fea244 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -337,12 +337,50 @@ return impact +/datum/seed/proc/generate_name() + var/prefix = "" + var/name = "" + if(prob(50)) //start with a prefix. + //These are various plant/mushroom genuses. + //I realize these might not be entirely accurate, but it could facilitate RP. + var/list/possible_prefixes + if(seed_noun == "cuttings" || seed_noun == "seeds" || (seed_noun == "nodes" && prob(50))) + possible_prefixes = list("amelanchier", "saskatoon", + "magnolia", "angiosperma", "osmunda", "scabiosa", "spigelia", "psydrax", "chastetree", + "strychnos", "treebine", "caper", "justica", "ragwortus", "everlasting", "combretum", + "loganiaceae", "gelsemium", "logania", "sabadilla", "neuburgia", "canthium", "rytigynia", + "chaste", "vitex", "cissus", "capparis", "senecio", "curry", "cycad", "liverwort", "charophyta", + "glaucophyte", "pinidae", "vascular", "embryophyte", "lillopsida") + else + possible_prefixes = list("bisporus", "bitorquis", "campestris", "crocodilinus", "agaricus", + "armillaria", "matsutake", "mellea", "ponderosa", "auricularia", "auricala", + "polytricha", "boletus", "badius", "edulis", "mirabilis", "zelleri", + "calvatia", "gigantea", "clitopilis", "prumulus", "entoloma", "abortivum", + "suillus", "tuber", "aestivum", "volvacea", "delica", "russula", "rozites") + + possible_prefixes |= list("butter", "shad", "sugar", "june", "wild", "rigus", "curry", "hard", "soft", "dark", "brick", "stone", "red", "brown", + "black", "white", "paper", "slippery", "honey", "bitter") + prefix = pick(possible_prefixes) + + var/num = rand(2,5) + var/list/possible_name = list("rhon", "cus", "quam", "met", "eget", "was", "reg", "zor", "fra", "rat", "sho", "ghen", "pa", + "eir", "lip", "sum", "lor", "em", "tem", "por", "invi", "dunt", "ut", "la", "bore", "mag", "na", + "al", "i", "qu", "yam", "er", "at", "sed", "di", "am", "vol", "up", "tua", "at", "ve", "ro", "eos", + "et", "ac", "cus") + for(var/i in 1 to num) + var/syl = pick(possible_name) + possible_name -= syl + name += syl + + if(prefix) + name = "[prefix] [name]" + seed_name = name + display_name = name + //Creates a random seed. MAKE SURE THE LINE HAS DIVERGED BEFORE THIS IS CALLED. /datum/seed/proc/randomize() roundstart = 0 - seed_name = "strange plant" // TODO: name generator. - display_name = "strange plants" // TODO: name generator. mysterious = 1 seed_noun = pick("spores","nodes","cuttings","seeds") @@ -390,49 +428,18 @@ var/additional_chems = rand(0,5) if(additional_chems) - var/list/possible_chems = list( - "woodpulp", - "bicaridine", - "hyperzine", - "cryoxadone", - "blood", - "water", - "potassium", - "plasticide", - "mutationtoxin", - "amutationtoxin", - "inaprovaline", - "space_drugs", - "paroxetine", - "mercury", - "sugar", - "radium", - "ryetalyn", - "alkysine", - "thermite", - "tramadol", - "cryptobiolin", - "dermaline", - "dexalin", - "phoron", - "synaptizine", - "impedrezene", - "hyronalin", - "peridaxon", - "toxin", - "rezadone", - "ethylredoxrazine", - "slimejelly", - "cyanide", - "mindbreaker", - "stoxin" + + var/list/banned_chems = list( + "adminordrazine", + "nutriment" ) for(var/x=1;x<=additional_chems;x++) - if(!possible_chems.len) - break - var/new_chem = pick(possible_chems) - possible_chems -= new_chem + + var/new_chem = pick(chemical_reagents_list) + if(new_chem in banned_chems) + continue + banned_chems += new_chem chems[new_chem] = list(rand(1,10),rand(10,20)) if(prob(90)) @@ -487,6 +494,8 @@ set_trait(TRAIT_MATURATION,rand(5,15)) set_trait(TRAIT_PRODUCTION,get_trait(TRAIT_MATURATION)+rand(2,5)) + generate_name() + //Returns a key corresponding to an entry in the global seed list. /datum/seed/proc/get_mutant_variant() if(!mutants || !mutants.len || get_trait(TRAIT_IMMUTABLE) > 0) return 0 diff --git a/code/modules/hydroponics/seed_datums.dm b/code/modules/hydroponics/seed_datums.dm index 157d4acbd3..6e88aabee5 100644 --- a/code/modules/hydroponics/seed_datums.dm +++ b/code/modules/hydroponics/seed_datums.dm @@ -550,6 +550,25 @@ set_trait(TRAIT_WATER_CONSUMPTION, 6) set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) +/datum/seed/flower/lavender + name = "lavender" + seed_name = "lavender" + display_name = "lavender" + chems = list("nutriment" = list(1,20), "bicaridine" = list(1,10)) + +/datum/seed/flower/lavender/New() + ..() + set_trait(TRAIT_MATURATION,7) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,5) + set_trait(TRAIT_PRODUCT_ICON,"flower6") + set_trait(TRAIT_PRODUCT_COLOUR,"#B57EDC") + set_trait(TRAIT_PLANT_COLOUR,"#6B8C5E") + set_trait(TRAIT_PLANT_ICON,"flower4") + set_trait(TRAIT_IDEAL_LIGHT, 7) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.05) + set_trait(TRAIT_WATER_CONSUMPTION, 0.5) + //Grapes/varieties /datum/seed/grapes name = "grapes" diff --git a/code/modules/hydroponics/seed_packets.dm b/code/modules/hydroponics/seed_packets.dm index e14fbd792b..7c1b70c4c7 100644 --- a/code/modules/hydroponics/seed_packets.dm +++ b/code/modules/hydroponics/seed_packets.dm @@ -197,6 +197,9 @@ var/global/list/plant_seed_sprites = list() /obj/item/seeds/sunflowerseed seed_type = "sunflowers" +/obj/item/seeds/lavenderseed + seed_type = "lavender" + /obj/item/seeds/brownmold seed_type = "mold" diff --git a/code/modules/hydroponics/seed_storage.dm b/code/modules/hydroponics/seed_storage.dm index 448173104b..fe0a178919 100644 --- a/code/modules/hydroponics/seed_storage.dm +++ b/code/modules/hydroponics/seed_storage.dm @@ -40,12 +40,91 @@ /obj/machinery/seed_storage/garden name = "Garden seed storage" scanner = list("stats") - starting_seeds = list(/obj/item/seeds/appleseed = 3, /obj/item/seeds/bananaseed = 3, /obj/item/seeds/berryseed = 3, /obj/item/seeds/cabbageseed = 3, /obj/item/seeds/carrotseed = 3, /obj/item/seeds/chantermycelium = 3, /obj/item/seeds/cherryseed = 3, /obj/item/seeds/chiliseed = 3, /obj/item/seeds/cocoapodseed = 3, /obj/item/seeds/cornseed = 3, /obj/item/seeds/eggplantseed = 3, /obj/item/seeds/grapeseed = 3, /obj/item/seeds/grassseed = 3, /obj/item/seeds/replicapod = 3, /obj/item/seeds/lemonseed = 3, /obj/item/seeds/limeseed = 3, /obj/item/seeds/mtearseed = 2, /obj/item/seeds/orangeseed = 3, /obj/item/seeds/peanutseed = 3, /obj/item/seeds/plumpmycelium = 3, /obj/item/seeds/poppyseed = 3, /obj/item/seeds/potatoseed = 3, /obj/item/seeds/pumpkinseed = 3, /obj/item/seeds/riceseed = 3, /obj/item/seeds/soyaseed = 3, /obj/item/seeds/sugarcaneseed = 3, /obj/item/seeds/sunflowerseed = 3, /obj/item/seeds/shandseed = 2, /obj/item/seeds/tobaccoseed = 3, /obj/item/seeds/tomatoseed = 3, /obj/item/seeds/towermycelium = 3, /obj/item/seeds/watermelonseed = 3, /obj/item/seeds/wheatseed = 3, /obj/item/seeds/whitebeetseed = 3) + starting_seeds = list( + /obj/item/seeds/appleseed = 3, + /obj/item/seeds/bananaseed = 3, + /obj/item/seeds/berryseed = 3, + /obj/item/seeds/cabbageseed = 3, + /obj/item/seeds/carrotseed = 3, + /obj/item/seeds/chantermycelium = 3, + /obj/item/seeds/cherryseed = 3, + /obj/item/seeds/chiliseed = 3, + /obj/item/seeds/cocoapodseed = 3, + /obj/item/seeds/cornseed = 3, + /obj/item/seeds/eggplantseed = 3, + /obj/item/seeds/grapeseed = 3, + /obj/item/seeds/grassseed = 3, + /obj/item/seeds/replicapod = 3, + /obj/item/seeds/lemonseed = 3, + /obj/item/seeds/limeseed = 3, + /obj/item/seeds/mtearseed = 2, + /obj/item/seeds/orangeseed = 3, + /obj/item/seeds/peanutseed = 3, + /obj/item/seeds/plumpmycelium = 3, + /obj/item/seeds/poppyseed = 3, + /obj/item/seeds/potatoseed = 3, + /obj/item/seeds/pumpkinseed = 3, + /obj/item/seeds/riceseed = 3, + /obj/item/seeds/soyaseed = 3, + /obj/item/seeds/sugarcaneseed = 3, + /obj/item/seeds/sunflowerseed = 3, + /obj/item/seeds/shandseed = 2, + /obj/item/seeds/tobaccoseed = 3, + /obj/item/seeds/tomatoseed = 3, + /obj/item/seeds/towermycelium = 3, + /obj/item/seeds/watermelonseed = 3, + /obj/item/seeds/wheatseed = 3, + /obj/item/seeds/whitebeetseed = 3, + /obj/item/seeds/lavenderseed = 3 + ) /obj/machinery/seed_storage/xenobotany name = "Xenobotany seed storage" scanner = list("stats", "produce", "soil", "temperature", "light") - starting_seeds = list(/obj/item/seeds/ambrosiavulgarisseed = 3, /obj/item/seeds/appleseed = 3, /obj/item/seeds/amanitamycelium = 2, /obj/item/seeds/bananaseed = 3, /obj/item/seeds/berryseed = 3, /obj/item/seeds/cabbageseed = 3, /obj/item/seeds/carrotseed = 3, /obj/item/seeds/chantermycelium = 3, /obj/item/seeds/cherryseed = 3, /obj/item/seeds/chiliseed = 3, /obj/item/seeds/cocoapodseed = 3, /obj/item/seeds/cornseed = 3, /obj/item/seeds/replicapod = 3, /obj/item/seeds/eggplantseed = 3, /obj/item/seeds/glowshroom = 2, /obj/item/seeds/grapeseed = 3, /obj/item/seeds/grassseed = 3, /obj/item/seeds/lemonseed = 3, /obj/item/seeds/libertymycelium = 2, /obj/item/seeds/limeseed = 3, /obj/item/seeds/mtearseed = 2, /obj/item/seeds/nettleseed = 2, /obj/item/seeds/orangeseed = 3, /obj/item/seeds/peanutseed = 3, /obj/item/seeds/plastiseed = 3, /obj/item/seeds/plumpmycelium = 3, /obj/item/seeds/poppyseed = 3, /obj/item/seeds/potatoseed = 3, /obj/item/seeds/pumpkinseed = 3, /obj/item/seeds/reishimycelium = 2, /obj/item/seeds/riceseed = 3, /obj/item/seeds/soyaseed = 3, /obj/item/seeds/sugarcaneseed = 3, /obj/item/seeds/sunflowerseed = 3, /obj/item/seeds/shandseed = 2, /obj/item/seeds/tobaccoseed = 3, /obj/item/seeds/tomatoseed = 3, /obj/item/seeds/towermycelium = 3, /obj/item/seeds/watermelonseed = 3, /obj/item/seeds/wheatseed = 3, /obj/item/seeds/whitebeetseed = 3) + starting_seeds = list( + /obj/item/seeds/ambrosiavulgarisseed = 3, + /obj/item/seeds/appleseed = 3, + /obj/item/seeds/amanitamycelium = 2, + /obj/item/seeds/bananaseed = 3, + /obj/item/seeds/berryseed = 3, + /obj/item/seeds/cabbageseed = 3, + /obj/item/seeds/carrotseed = 3, + /obj/item/seeds/chantermycelium = 3, + /obj/item/seeds/cherryseed = 3, + /obj/item/seeds/chiliseed = 3, + /obj/item/seeds/cocoapodseed = 3, + /obj/item/seeds/cornseed = 3, + /obj/item/seeds/replicapod = 3, + /obj/item/seeds/eggplantseed = 3, + /obj/item/seeds/glowshroom = 2, + /obj/item/seeds/grapeseed = 3, + /obj/item/seeds/grassseed = 3, + /obj/item/seeds/lemonseed = 3, + /obj/item/seeds/libertymycelium = 2, + /obj/item/seeds/limeseed = 3, + /obj/item/seeds/mtearseed = 2, + /obj/item/seeds/nettleseed = 2, + /obj/item/seeds/orangeseed = 3, + /obj/item/seeds/peanutseed = 3, + /obj/item/seeds/plastiseed = 3, + /obj/item/seeds/plumpmycelium = 3, + /obj/item/seeds/poppyseed = 3, + /obj/item/seeds/potatoseed = 3, + /obj/item/seeds/pumpkinseed = 3, + /obj/item/seeds/reishimycelium = 2, + /obj/item/seeds/riceseed = 3, + /obj/item/seeds/soyaseed = 3, + /obj/item/seeds/sugarcaneseed = 3, + /obj/item/seeds/sunflowerseed = 3, + /obj/item/seeds/shandseed = 2, + /obj/item/seeds/tobaccoseed = 3, + /obj/item/seeds/tomatoseed = 3, + /obj/item/seeds/towermycelium = 3, + /obj/item/seeds/watermelonseed = 3, + /obj/item/seeds/wheatseed = 3, + /obj/item/seeds/whitebeetseed = 3, + /obj/item/seeds/lavenderseed = 3 + ) /obj/machinery/seed_storage/attack_hand(mob/user as mob) user.set_machine(src) diff --git a/code/modules/hydroponics/trays/tray_reagents.dm b/code/modules/hydroponics/trays/tray_reagents.dm index 818bbd3b15..ba2013812a 100644 --- a/code/modules/hydroponics/trays/tray_reagents.dm +++ b/code/modules/hydroponics/trays/tray_reagents.dm @@ -48,20 +48,6 @@ toxicity = 8 pest_kill_str = 7 -/obj/item/weapon/material/minihoe // -- Numbers - name = "mini hoe" - desc = "It's used for removing weeds or scratching your back." - icon = 'icons/obj/weapons.dmi' - icon_state = "hoe" - item_state = "hoe" - flags = CONDUCT | NOBLUDGEON - force = 5.0 - throwforce = 7.0 - w_class = 2.0 - matter = list(DEFAULT_WALL_MATERIAL = 50) - attack_verb = list("slashed", "sliced", "cut", "clawed") - - // ************************************* // Weedkiller defines for hydroponics // ************************************* @@ -92,4 +78,4 @@ icon = 'icons/obj/chemical.dmi' icon_state = "bottle15" toxicity = 8 - weed_kill_str = 7 \ No newline at end of file + weed_kill_str = 7 diff --git a/code/modules/hydroponics/trays/tray_update_icons.dm b/code/modules/hydroponics/trays/tray_update_icons.dm index dc69e5448e..47e6b82ba6 100644 --- a/code/modules/hydroponics/trays/tray_update_icons.dm +++ b/code/modules/hydroponics/trays/tray_update_icons.dm @@ -3,7 +3,7 @@ // Update name. if(seed) if(mechanical) - name = "[base_name] (#[seed.uid])" + name = "[base_name] ([seed.seed_name])" else name = "[seed.seed_name]" else diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm index 3dbb74d933..ba96ada8b7 100644 --- a/code/modules/library/lib_items.dm +++ b/code/modules/library/lib_items.dm @@ -256,6 +256,7 @@ user.visible_message("You open up the book and show it to [M]. ", \ " [user] opens up a book and shows it to [M]. ") M << browse("Penned by [author].
" + "[dat]", "window=book") + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //to prevent spam /* diff --git a/code/modules/materials/material_recipes.dm b/code/modules/materials/material_recipes.dm index cf83cef983..aebcacc91b 100644 --- a/code/modules/materials/material_recipes.dm +++ b/code/modules/materials/material_recipes.dm @@ -17,6 +17,7 @@ recipes += new/datum/stack_recipe("[display_name] stool", /obj/item/weapon/stool, one_per_turf = 1, on_floor = 1, supplied_material = "[name]") recipes += new/datum/stack_recipe("[display_name] chair", /obj/structure/bed/chair, one_per_turf = 1, on_floor = 1, supplied_material = "[name]") recipes += new/datum/stack_recipe("[display_name] bed", /obj/structure/bed, 2, one_per_turf = 1, on_floor = 1, supplied_material = "[name]") + recipes += new/datum/stack_recipe("[display_name] double bed", /obj/structure/bed/double, 4, one_per_turf = 1, on_floor = 1, supplied_material = "[name]") if(hardness>50) recipes += new/datum/stack_recipe("[display_name] fork", /obj/item/weapon/material/kitchen/utensil/fork/plastic, 1, on_floor = 1, supplied_material = "[name]") diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index a7c4384f4f..2119896c3f 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -43,7 +43,7 @@ var/global/list/image/ghost_sightless_images = list() //this is a list of images "Bland" = "ghost", "Robed-B" = "ghost1", "Robed-BAlt" = "ghost2", - "Corgi" = "ghostian", + "Corgi" = "corgi", "King" = "ghostking", "Shade" = "shade", "Hecate" = "ghost-narsie", @@ -65,7 +65,15 @@ var/global/list/image/ghost_sightless_images = list() //this is a list of images "Pink Elemental" = "magicPink", "Orange Elemental" = "magicOrange", "Green Elemental" = "magicGreen", - "Daemon" = "daemon" + "Daemon" = "daemon", + "Guard Spider" = "guard", + "Hunter Spider" = "hunter", + "Nurse Spider" = "nurse", + "Carp" = "carp", + "Space Bear" = "bear", + "Rogue Drone" = "drone", + "ED-209" = "ed209", + "Beepsky" = "secbot" ) /mob/observer/dead/New(mob/body) @@ -817,6 +825,7 @@ mob/observer/dead/MayRespawn(var/feedback = 0) set name = "Choose Sprite" icon = 'icons/mob/ghost.dmi' + overlays.Cut() var/choice var/finalized = "No" while(finalized == "No" && src.client) diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm index 9dfd3deb44..ada597f46b 100644 --- a/code/modules/mob/dead/observer/say.dm +++ b/code/modules/mob/dead/observer/say.dm @@ -11,9 +11,6 @@ src << "\red You cannot talk in deadchat (muted)." return - if (src.client.handle_spam_prevention(message,MUTE_DEADCHAT)) - return - . = src.say_dead(message) @@ -33,9 +30,6 @@ src << "\red You cannot emote in deadchat (muted)." return - if(src.client.handle_spam_prevention(message, MUTE_DEADCHAT)) - return - . = src.emote_dead(message) /* diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index 9d979787c5..ad71cebe1f 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -233,3 +233,8 @@ var/list/slot_equipment_priority = list( \ //if(hasvar(src,"r_hand")) if(src:r_hand) items += src:r_hand return items + +/mob/proc/delete_inventory() + for(var/entry in get_equipped_items()) + drop_from_inventory(entry) + qdel(entry) diff --git a/code/modules/mob/language/language.dm b/code/modules/mob/language/language.dm index 223b01e110..372409b443 100644 --- a/code/modules/mob/language/language.dm +++ b/code/modules/mob/language/language.dm @@ -10,8 +10,8 @@ var/speech_verb = "says" // 'says', 'hisses', 'farts'. var/ask_verb = "asks" // Used when sentence ends in a ? var/exclaim_verb = "exclaims" // Used when sentence ends in a ! - var/whisper_verb = "whispers" // Optional. When not specified speech_verb + quietly/softly is used instead. - var/signlang_verb = list("signs") // list of emotes that might be displayed if this language has NONVERBAL or SIGNLANG flags + var/whisper_verb // Optional. When not specified speech_verb + quietly/softly is used instead. + var/signlang_verb = list("signs", "gestures") // list of emotes that might be displayed if this language has NONVERBAL or SIGNLANG flags var/colour = "body" // CSS style to use for strings in this language. var/key = "x" // Character used to speak in language eg. :o for Unathi. var/flags = 0 // Various language flags. diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm index 5679cc1a46..0ad82ae384 100644 --- a/code/modules/mob/living/bot/bot.dm +++ b/code/modules/mob/living/bot/bot.dm @@ -268,9 +268,6 @@ /mob/living/bot/proc/explode() qdel(src) -/mob/living/bot/attack_throat() - return - /******************************************************************/ // Navigation procs // Used for A-star pathfinding @@ -337,3 +334,4 @@ //if((dir & EAST ) && (D.dir & (NORTH|SOUTH))) return !D.check_access(ID) else return !D.check_access(ID) // it's a real, air blocking door return 0 + diff --git a/code/modules/mob/living/carbon/alien/emote.dm b/code/modules/mob/living/carbon/alien/emote.dm index 62fa2cf108..e31a293047 100644 --- a/code/modules/mob/living/carbon/alien/emote.dm +++ b/code/modules/mob/living/carbon/alien/emote.dm @@ -18,8 +18,6 @@ if (client.prefs.muted & MUTE_IC) src << "\red You cannot send IC messages (muted)." return - if (src.client.handle_spam_prevention(message,MUTE_IC)) - return if (stat) return if(!(message)) diff --git a/code/modules/mob/living/carbon/brain/emote.dm b/code/modules/mob/living/carbon/brain/emote.dm index f29167f1f3..5725dd4ec7 100644 --- a/code/modules/mob/living/carbon/brain/emote.dm +++ b/code/modules/mob/living/carbon/brain/emote.dm @@ -19,8 +19,6 @@ if (client.prefs.muted & MUTE_IC) src << "\red You cannot send IC messages (muted)." return - if (src.client.handle_spam_prevention(message,MUTE_IC)) - return if (stat) return if(!(message)) diff --git a/code/modules/mob/living/carbon/brain/posibrain.dm b/code/modules/mob/living/carbon/brain/posibrain.dm index c065cbdc54..c61659261d 100644 --- a/code/modules/mob/living/carbon/brain/posibrain.dm +++ b/code/modules/mob/living/carbon/brain/posibrain.dm @@ -74,6 +74,7 @@ /obj/item/device/mmi/digital/posibrain/proc/reset_search() //We give the players sixty seconds to decide, then reset the timer. if(src.brainmob && src.brainmob.key) return + world.log << "Resetting Posibrain: [brainmob][brainmob ? ", [brainmob.key]" : ""]" src.searching = 0 icon_state = "posibrain" diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm new file mode 100644 index 0000000000..dd9e4f28b9 --- /dev/null +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -0,0 +1,98 @@ + +//Called when the mob is hit with an item in combat. +/mob/living/carbon/resolve_item_attack(obj/item/I, mob/living/user, var/effective_force, var/hit_zone) + if(check_attack_throat(I, user)) + return null + ..() + +/mob/living/carbon/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/hit_zone) + if(!effective_force || blocked >= 100) + return 0 + + //Hulk modifier + if(HULK in user.mutations) + effective_force *= 2 + + //Apply weapon damage + var/weapon_sharp = is_sharp(I) + var/weapon_edge = has_edge(I) + if(prob(getarmor(hit_zone, "melee"))) //melee armour provides a chance to turn sharp/edge weapon attacks into blunt ones + weapon_sharp = 0 + weapon_edge = 0 + + apply_damage(effective_force, I.damtype, hit_zone, blocked, sharp=weapon_sharp, edge=weapon_edge, used_weapon=I) + + //Melee weapon embedded object code. + if (I && I.damtype == BRUTE && !I.anchored && !is_robot_module(I)) + var/damage = effective_force + if (blocked) + damage *= (100 - blocked)/100 + + //blunt objects should really not be embedding in things unless a huge amount of force is involved + var/embed_chance = weapon_sharp? damage/I.w_class : damage/(I.w_class*3) + var/embed_threshold = weapon_sharp? 5*I.w_class : 15*I.w_class + + //Sharp objects will always embed if they do enough damage. + if((weapon_sharp && damage > (10*I.w_class)) || (damage > embed_threshold && prob(embed_chance))) + src.embed(I, hit_zone) + + return 1 + +// Attacking someone with a weapon while they are neck-grabbed +/mob/living/carbon/proc/check_attack_throat(obj/item/W, mob/user) + if(user.a_intent == I_HURT) + for(var/obj/item/weapon/grab/G in src.grabbed_by) + if(G.assailant == user && G.state >= GRAB_NECK) + if(attack_throat(W, G, user)) + return 1 + return 0 + +// Knifing +/mob/living/carbon/proc/attack_throat(obj/item/W, obj/item/weapon/grab/G, mob/user) + + if(!W.edge || !W.force || W.damtype != BRUTE) + return 0 //unsuitable weapon + + user.visible_message("\The [user] begins to slit [src]'s throat with \the [W]!") + + user.next_move = world.time + 20 //also should prevent user from triggering this repeatedly + if(!do_after(user, 20)) + return 0 + if(!(G && G.assailant == user && G.affecting == src)) //check that we still have a grab + return 0 + + var/damage_mod = 1 + //presumably, if they are wearing a helmet that stops pressure effects, then it probably covers the throat as well + var/obj/item/clothing/head/helmet = get_equipped_item(slot_head) + if(istype(helmet) && (helmet.body_parts_covered & HEAD) && (helmet.flags & STOPPRESSUREDAMAGE)) + //we don't do an armor_check here because this is not an impact effect like a weapon swung with momentum, that either penetrates or glances off. + damage_mod = 1.0 - (helmet.armor["melee"]/100) + + var/total_damage = 0 + for(var/i in 1 to 3) + var/damage = min(W.force*1.5, 20)*damage_mod + apply_damage(damage, W.damtype, "head", 0, sharp=W.sharp, edge=W.edge) + total_damage += damage + + var/oxyloss = total_damage + if(total_damage >= 40) //threshold to make someone pass out + oxyloss = 60 // Brain lacks oxygen immediately, pass out + + adjustOxyLoss(min(oxyloss, 100 - getOxyLoss())) //don't put them over 100 oxyloss + + if(total_damage) + if(oxyloss >= 40) + user.visible_message("\The [user] slit [src]'s throat open with \the [W]!") + else + user.visible_message("\The [user] cut [src]'s neck with \the [W]!") + + if(W.hitsound) + playsound(loc, W.hitsound, 50, 1, -1) + + G.last_action = world.time + flick(G.hud.icon_state, G.hud) + + user.attack_log += "\[[time_stamp()]\] Knifed [name] ([ckey]) with [W.name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(W.damtype)])" + src.attack_log += "\[[time_stamp()]\] Got knifed by [user.name] ([user.ckey]) with [W.name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(W.damtype)])" + msg_admin_attack("[key_name(user)] knifed [key_name(src)] with [W.name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(W.damtype)])" ) + return 1 \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 6a45483251..8cb9cca614 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -36,12 +36,13 @@ animate_tail_stop() //Handle brain slugs. - var/obj/item/organ/external/head = get_organ(BP_HEAD) + var/obj/item/organ/external/Hd = get_organ(BP_HEAD) var/mob/living/simple_animal/borer/B - for(var/I in head.implants) - if(istype(I,/mob/living/simple_animal/borer)) - B = I + if(Hd) + for(var/I in Hd.implants) + if(istype(I,/mob/living/simple_animal/borer)) + B = I if(B) if(!B.ckey && ckey && B.controlling) B.ckey = ckey diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index 87c0524571..4488f65388 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -112,8 +112,6 @@ if (client.prefs.muted & MUTE_IC) src << "\red You cannot send IC messages (muted)." return - if (src.client.handle_spam_prevention(message,MUTE_IC)) - return if (stat) return if(!(message)) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 12ad8359db..53d12e8b7d 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -4,26 +4,69 @@ var/skipjumpsuit = 0 var/skipshoes = 0 var/skipmask = 0 + var/skipears = 0 var/skipeyes = 0 var/skipface = 0 + var/skipchest = 0 + var/skipgroin = 0 + var/skiphands = 0 + var/skiplegs = 0 + var/skiparms = 0 + var/skipfeet = 0 + + var/looks_synth = looksSynthetic() //exosuits and helmets obscure our view and stuff. if(wear_suit) - skipgloves = wear_suit.flags_inv & HIDEGLOVES - skipsuitstorage = wear_suit.flags_inv & HIDESUITSTORAGE - skipjumpsuit = wear_suit.flags_inv & HIDEJUMPSUIT - skipshoes = wear_suit.flags_inv & HIDESHOES + skipsuitstorage |= wear_suit.flags_inv & HIDESUITSTORAGE + if(wear_suit.flags_inv & HIDEJUMPSUIT) + skiparms |= 1 + skiplegs |= 1 + skipchest |= 1 + skipgroin |= 1 + if(wear_suit.flags_inv & HIDESHOES) + skipshoes |= 1 + skipfeet |= 1 + if(wear_suit.flags_inv & HIDEGLOVES) + skipgloves |= 1 + skiphands |= 1 + + if(w_uniform) + skiplegs |= w_uniform.body_parts_covered & LEGS + skiparms |= w_uniform.body_parts_covered & ARMS + skipchest |= w_uniform.body_parts_covered & UPPER_TORSO + skipgroin |= w_uniform.body_parts_covered & LOWER_TORSO + + if(gloves) + skiphands |= gloves.body_parts_covered & HANDS + + if(shoes) + skipfeet |= shoes.body_parts_covered & FEET if(head) - skipmask = head.flags_inv & HIDEMASK - skipeyes = head.flags_inv & HIDEEYES - skipears = head.flags_inv & HIDEEARS - skipface = head.flags_inv & HIDEFACE + skipmask |= head.flags_inv & HIDEMASK + skipeyes |= head.flags_inv & HIDEEYES + skipears |= head.flags_inv & HIDEEARS + skipface |= head.flags_inv & HIDEFACE if(wear_mask) skipface |= wear_mask.flags_inv & HIDEFACE + //This is what hides what + var/list/hidden = list( + BP_GROIN = skipgroin, + BP_TORSO = skipchest, + BP_HEAD = skipface, + BP_L_ARM = skiparms, + BP_R_ARM = skiparms, + BP_L_HAND= skiphands, + BP_R_HAND= skiphands, + BP_L_FOOT= skipfeet, + BP_R_FOOT= skipfeet, + BP_L_LEG = skiplegs, + BP_R_LEG = skiplegs) + var/msg = "*---------*\nThis is " var/datum/gender/T = gender_datums[get_gender()] @@ -39,9 +82,8 @@ msg += "[src.name]" - var/is_synth = isSynthetic() if(!(skipjumpsuit && skipface)) - if(is_synth) + if(looks_synth) var/use_gender = "a synthetic" if(gender == MALE) use_gender = "an android" @@ -124,8 +166,6 @@ else if(blood_DNA) msg += "[T.He] [T.has] [(hand_blood_color != SYNTH_BLOOD_COLOUR) ? "blood" : "oil"]-stained hands!\n" - //handcuffed? - //handcuffed? if(handcuffed) if(istype(handcuffed, /obj/item/weapon/handcuffs/cable)) @@ -273,15 +313,17 @@ for(var/obj/item/organ/external/temp in organs) if(temp) + if((temp.organ_tag in hidden) && hidden[temp.organ_tag]) + continue //Organ is hidden, don't talk about it if(temp.status & ORGAN_DESTROYED) wound_flavor_text["[temp.name]"] = "[T.He] [T.is] missing [T.his] [temp.name].\n" continue - if(!is_synth && temp.status & ORGAN_ROBOT) + if(!looks_synth && temp.robotic == ORGAN_ROBOT) if(!(temp.brute_dam + temp.burn_dam)) - wound_flavor_text["[temp.name]"] = "[T.He] [T.has] a [temp.name]!\n" - continue + wound_flavor_text["[temp.name]"] = "[T.He] [T.has] a [temp.name].\n" else - wound_flavor_text["[temp.name]"] = "[T.He] [T.has] a [temp.name]. It has[temp.get_wounds_desc()]!\n" + wound_flavor_text["[temp.name]"] = "[T.He] [T.has] a [temp.name] with [temp.get_wounds_desc()]!\n" + continue else if(temp.wounds.len > 0 || temp.open) if(temp.is_stump() && temp.parent_organ && organs_by_name[temp.parent_organ]) var/obj/item/organ/external/parent = organs_by_name[temp.parent_organ] diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 47853089cf..7736a66657 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1030,7 +1030,7 @@ src << msg organ.take_damage(rand(1,3), 0, 0) - if(!(organ.status & ORGAN_ROBOT) && !should_have_organ(O_HEART)) //There is no blood in protheses. + if(!(organ.robotic >= ORGAN_ROBOT) && !(should_have_organ(O_HEART))) //There is no blood in protheses. organ.status |= ORGAN_BLEEDING src.adjustToxLoss(rand(1,3)) @@ -1140,8 +1140,6 @@ qdel(hud_used) hud_used = new /datum/hud(src) - full_prosthetic = null - if(species) return 1 else @@ -1216,7 +1214,7 @@ if(!affecting) . = 0 fail_msg = "They are missing that limb." - else if (affecting.status & ORGAN_ROBOT) + else if (affecting.robotic >= ORGAN_ROBOT) . = 0 fail_msg = "That limb is robotic." else @@ -1444,7 +1442,7 @@ else if(organ_check in list(O_LIVER, O_KIDNEYS)) affecting = organs_by_name[BP_GROIN] - if(affecting && (affecting.status & ORGAN_ROBOT)) + if(affecting && (affecting.robotic >= ORGAN_ROBOT)) return 0 return (species && species.has_organ[organ_check]) diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index 56a47f918b..7cc3156f18 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -310,23 +310,6 @@ updatehealth() return 1 -/mob/living/carbon/human/proc/attack_joint(var/obj/item/W, var/mob/living/user, var/def_zone) - if(!def_zone) def_zone = user.zone_sel.selecting - var/target_zone = get_zone_with_miss_chance(check_zone(def_zone), src) - - if(user == src) // Attacking yourself can't miss - target_zone = user.zone_sel.selecting - if(!target_zone) - return null - var/obj/item/organ/external/organ = get_organ(check_zone(target_zone)) - if(!organ || (organ.dislocated == 2) || (organ.dislocated == -1)) - return null - var/dislocation_str - if(prob(W.force)) - dislocation_str = "[src]'s [organ.joint] [pick("gives way","caves in","crumbles","collapses")]!" - organ.dislocate(1) - return dislocation_str - //Used to attack a joint through grabbing /mob/living/carbon/human/proc/grab_joint(var/mob/living/user, var/def_zone) var/has_grab = 0 diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index e0451046ae..994d9dd723 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -9,8 +9,8 @@ var/total_burn = 0 var/total_brute = 0 for(var/obj/item/organ/external/O in organs) //hardcoded to streamline things a bit - if((O.status & ORGAN_ROBOT) && !O.vital) - continue // Non-vital robot limbs don't count towards shock and crit + if((O.robotic >= ORGAN_ROBOT) && !O.vital) + continue //*non-vital* robot limbs don't count towards shock and crit total_brute += O.brute_dam total_burn += O.burn_dam @@ -67,7 +67,7 @@ /mob/living/carbon/human/getBruteLoss() var/amount = 0 for(var/obj/item/organ/external/O in organs) - if((O.status & ORGAN_ROBOT) && !O.vital) + if(O.robotic >= ORGAN_ROBOT) continue //robot limbs don't count towards shock and crit amount += O.brute_dam return amount @@ -75,7 +75,7 @@ /mob/living/carbon/human/getFireLoss() var/amount = 0 for(var/obj/item/organ/external/O in organs) - if((O.status & ORGAN_ROBOT) && !O.vital) + if(O.robotic >= ORGAN_ROBOT) continue //robot limbs don't count towards shock and crit amount += O.burn_dam return amount @@ -106,7 +106,7 @@ O.take_damage(amount, 0, sharp=is_sharp(damage_source), edge=has_edge(damage_source), used_weapon=damage_source) else //if you don't want to heal robot organs, they you will have to check that yourself before using this proc. - O.heal_damage(-amount, 0, internal=0, robo_repair=(O.status & ORGAN_ROBOT)) + O.heal_damage(-amount, 0, internal=0, robo_repair=(O.robotic >= ORGAN_ROBOT)) BITSET(hud_updateflag, HEALTH_HUD) @@ -119,7 +119,7 @@ O.take_damage(0, amount, sharp=is_sharp(damage_source), edge=has_edge(damage_source), used_weapon=damage_source) else //if you don't want to heal robot organs, they you will have to check that yourself before using this proc. - O.heal_damage(0, -amount, internal=0, robo_repair=(O.status & ORGAN_ROBOT)) + O.heal_damage(0, -amount, internal=0, robo_repair=(O.robotic >= ORGAN_ROBOT)) BITSET(hud_updateflag, HEALTH_HUD) @@ -266,7 +266,6 @@ In most cases it makes more sense to use apply_damage() instead! And make sure t UpdateDamageIcon() BITSET(hud_updateflag, HEALTH_HUD) updatehealth() - speech_problem_flag = 1 //Heal MANY external organs, in random order @@ -288,7 +287,6 @@ In most cases it makes more sense to use apply_damage() instead! And make sure t parts -= picked updatehealth() BITSET(hud_updateflag, HEALTH_HUD) - speech_problem_flag = 1 if(update) UpdateDamageIcon() // damage MANY external organs, in random order diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index cfb5058c0a..614bbe1de8 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -57,7 +57,7 @@ emp_act msg_admin_attack("[src.name] ([src.ckey]) was disarmed by a stun effect") drop_from_inventory(c_hand) - if (affected.status & ORGAN_ROBOT) + if (affected.robotic >= ORGAN_ROBOT) emote("me", 1, "drops what they were holding, their [affected.name] malfunctioning!") else var/emote_scream = pick("screams in pain and ", "lets out a sharp cry and ", "cries out and ") @@ -139,67 +139,73 @@ emp_act if(.) return return 0 -//Returns 1 if the attack hit, 0 if it missed. -/mob/living/carbon/human/proc/attacked_by(var/obj/item/I, var/mob/living/user, var/def_zone) - if(!I || !user) return 0 +/mob/living/carbon/human/emp_act(severity) + for(var/obj/O in src) + if(!O) continue + O.emp_act(severity) + ..() - var/target_zone = def_zone? check_zone(def_zone) : get_zone_with_miss_chance(user.zone_sel.selecting, src) +/mob/living/carbon/human/resolve_item_attack(obj/item/I, mob/living/user, var/target_zone) + if(check_attack_throat(I, user)) + return null if(user == src) // Attacking yourself can't miss - target_zone = user.zone_sel.selecting - if(!target_zone) - visible_message("[user] misses [src] with \the [I]!") - return 0 + return target_zone - var/obj/item/organ/external/affecting = get_organ(target_zone) + var/hit_zone = get_zone_with_miss_chance(target_zone, src) + if(!hit_zone) + visible_message("\The [user] misses [src] with \the [I]!") + return null + + if(check_shields(I.force, I, user, target_zone, "the [I.name]")) + return + + var/obj/item/organ/external/affecting = get_organ(hit_zone) if (!affecting || affecting.is_stump()) user << "They are missing that limb!" + return null + + return hit_zone + +/mob/living/carbon/human/hit_with_weapon(obj/item/I, mob/living/user, var/effective_force, var/hit_zone) + var/obj/item/organ/external/affecting = get_organ(hit_zone) + if(!affecting) + return //should be prevented by attacked_with_item() but for sanity. + + visible_message("[src] has been [I.attack_verb.len? pick(I.attack_verb) : "attacked"] in the [affecting.name] with [I.name] by [user]!") + + var/blocked = run_armor_check(hit_zone, "melee", I.armor_penetration, "Your armor has protected your [affecting.name].", "Your armor has softened the blow to your [affecting.name].") + standard_weapon_hit_effects(I, user, effective_force, blocked, hit_zone) + + return blocked + +/mob/living/carbon/human/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/hit_zone) + var/obj/item/organ/external/affecting = get_organ(hit_zone) + if(!affecting) return 0 - var/effective_force = I.force - if(user.a_intent == "disarm") effective_force = round(I.force/2) - var/hit_area = affecting.name - - if((user != src) && check_shields(effective_force, I, user, target_zone, "the [I.name]")) - return 0 - - if(istype(I,/obj/item/weapon/card/emag)) - if(!(affecting.status & ORGAN_ROBOT)) - user << "\red That limb isn't robotic." - return - if(affecting.sabotaged) - user << "\red [src]'s [affecting.name] is already sabotaged!" + // Handle striking to cripple. + if(user.a_intent == I_DISARM) + effective_force /= 2 + if(..(I, user, effective_force, blocked, hit_zone)) + attack_joint(affecting, I, blocked) else - user << "\red You sneakily slide [I] into the dataport on [src]'s [affecting.name] and short out the safeties." - var/obj/item/weapon/card/emag/emag = I - emag.uses-- - affecting.sabotaged = 1 - return 1 + return 0 + else if(!..()) + return 0 - if(I.attack_verb.len) - visible_message("\red [src] has been [pick(I.attack_verb)] in the [hit_area] with [I.name] by [user]!") - else - visible_message("\red [src] has been attacked in the [hit_area] with [I.name] by [user]!") + if(effective_force > 10 || effective_force >= 5 && prob(33)) + forcesay(hit_appends) //forcesay checks stat already - var/armor = run_armor_check(affecting, "melee", I.armor_penetration, "Your armor has protected your [hit_area].", "Your armor has softened hit to your [hit_area].") - var/weapon_sharp = is_sharp(I) - var/weapon_edge = has_edge(I) - if ((weapon_sharp || weapon_edge) && prob(getarmor(target_zone, "melee"))) - weapon_sharp = 0 - weapon_edge = 0 + if(prob(25 + (effective_force * 2))) + if(!((I.damtype == BRUTE) || (I.damtype == HALLOSS))) + return - if(armor >= 100) return 0 - if(!effective_force) return 0 - var/Iforce = effective_force //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords) + if(!(I.flags & NOBLOODY)) + I.add_blood(src) - apply_damage(effective_force, I.damtype, affecting, armor, sharp=weapon_sharp, edge=weapon_edge, used_weapon=I) - - var/bloody = 0 - if(((I.damtype == BRUTE) || (I.damtype == HALLOSS)) && prob(25 + (effective_force * 2))) - I.add_blood(src) //Make the weapon bloody, not the person. -// if(user.hand) user.update_inv_l_hand() //updates the attacker's overlay for the (now bloodied) weapon -// else user.update_inv_r_hand() //removed because weapons don't have on-mob blood overlays + var/bloody = 0 if(prob(33)) bloody = 1 var/turf/location = loc @@ -212,49 +218,49 @@ emp_act H.bloody_hands(src) if(!stat) - if(headcheck(hit_area)) - //Harder to score a stun but if you do it lasts a bit longer - if(prob(effective_force)) - apply_effect(20, PARALYZE, armor) - visible_message("[src] [species.get_knockout_message(src)]") - else - //Easier to score a stun but lasts less time - if(prob(effective_force + 10)) - apply_effect(6, WEAKEN, armor) - visible_message("[src] has been knocked down!") + switch(hit_zone) + if("head")//Harder to score a stun but if you do it lasts a bit longer + if(prob(effective_force)) + apply_effect(20, PARALYZE, blocked) + visible_message("\The [src] has been knocked unconscious!") + if(bloody)//Apply blood + if(wear_mask) + wear_mask.add_blood(src) + update_inv_wear_mask(0) + if(head) + head.add_blood(src) + update_inv_head(0) + if(glasses && prob(33)) + glasses.add_blood(src) + update_inv_glasses(0) + if("chest")//Easier to score a stun but lasts less time + if(prob(effective_force + 10)) + apply_effect(6, WEAKEN, blocked) + visible_message("\The [src] has been knocked down!") + if(bloody) + bloody_body(src) - //Apply blood - if(bloody) - switch(hit_area) - if(BP_HEAD) - if(wear_mask) - wear_mask.add_blood(src) - update_inv_wear_mask(0) - if(head) - head.add_blood(src) - update_inv_head(0) - if(glasses && prob(33)) - glasses.add_blood(src) - update_inv_glasses(0) - if(BP_TORSO) - bloody_body(src) + return 1 - if(Iforce > 10 || Iforce >= 5 && prob(33)) - forcesay(hit_appends) //forcesay checks stat already +/mob/living/carbon/human/proc/attack_joint(var/obj/item/organ/external/organ, var/obj/item/W, var/blocked) + if(!organ || (organ.dislocated == 2) || (organ.dislocated == -1) || blocked >= 100) + return 0 + if(prob(W.force * (100 - blocked)/100)) + visible_message("[src]'s [organ.joint] [pick("gives way","caves in","crumbles","collapses")]!") + organ.dislocate(1) + return 1 + return 0 - //Melee weapon embedded object code. - if (I && I.damtype == BRUTE && !I.anchored && !is_robot_module(I)) - var/damage = effective_force - if (armor) - damage /= armor+1 - - //blunt objects should really not be embedding in things unless a huge amount of force is involved - var/embed_chance = weapon_sharp? damage/I.w_class : damage/(I.w_class*3) - var/embed_threshold = weapon_sharp? 5*I.w_class : 15*I.w_class - - //Sharp objects will always embed if they do enough damage. - if((weapon_sharp && damage > (10*I.w_class)) || (damage > embed_threshold && prob(embed_chance))) - affecting.embed(I) +/mob/living/carbon/human/emag_act(var/remaining_charges, mob/user, var/emag_source) + var/obj/item/organ/external/affecting = get_organ(user.zone_sel.selecting) + if(!affecting || !(affecting.status & ORGAN_ROBOT)) + user << "That limb isn't robotic." + return -1 + if(affecting.sabotaged) + user << "[src]'s [affecting.name] is already sabotaged!" + return -1 + user << "You sneakily slide [emag_source] into the dataport on [src]'s [affecting.name] and short out the safeties." + affecting.sabotaged = 1 return 1 //this proc handles being hit by a thrown atom diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 0a21d4d5bc..13f4f2f25a 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -31,11 +31,10 @@ var/age = 30 //Player's age (pure fluff) var/b_type = "A+" //Player's bloodtype + var/synthetic //If they are a synthetic (aka synthetic torso) - var/underwear_top = 1 //Which underwear the player wants - var/underwear_bottom = 1 - var/undershirt = 0 //Which undershirt the player wants. - var/socks = 0 //Which socks the player wants. + var/list/all_underwear = list() + var/list/all_underwear_metadata = list() var/backbag = 2 //Which backpack type the player has chosen. Nothing, Satchel or Backpack. var/pdachoice = 1 //Which PDA type the player has chosen. Default, Slim, or Old. @@ -69,8 +68,6 @@ var/voice = "" //Instead of new say code calling GetVoice() over and over and over, we're just going to ask this variable, which gets updated in Life() - var/speech_problem_flag = 0 - var/miming = null //Toggle for the mime's abilities. var/special_voice = "" // For changing our voice. Used by a symptom. @@ -85,7 +82,6 @@ var/list/flavor_texts = list() var/gunshot_residue var/pulling_punches // Are you trying not to hurt your opponent? - var/full_prosthetic // We are a robutt. var/robolimb_count = 0 // Number of robot limbs. mob_bump_flag = HUMAN diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index df8eead14e..7682f675b0 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -58,6 +58,38 @@ /mob/living/carbon/human/get_gender() return identifying_gender ? identifying_gender : gender +// This is the 'mechanical' check for synthetic-ness, not appearance +// Returns the company that made the synthetic +/mob/living/carbon/human/isSynthetic() + if(synthetic) return synthetic //Your synthetic-ness is not going away + var/obj/item/organ/external/T = organs_by_name[BP_TORSO] + if(T && T.robotic >= ORGAN_ROBOT) + src.verbs += /mob/living/carbon/human/proc/self_diagnostics + var/datum/robolimb/R = all_robolimbs[T.model] + synthetic = R + return synthetic + + return 0 + +// Would an onlooker know this person is synthetic? +// Based on sort of logical reasoning, 'Look at head, look at torso' +/mob/living/carbon/human/proc/looksSynthetic() + var/obj/item/organ/external/T = organs_by_name[BP_TORSO] + var/obj/item/organ/external/H = organs_by_name[BP_HEAD] + + //Look at their head + if(!head || !(head && (head.flags_inv & HIDEFACE))) + if(H && H.robotic == ORGAN_ROBOT) //Exactly robotic, not higher as lifelike is higher + return 1 + + //Look at their torso + if(!wear_suit || (wear_suit && !(wear_suit.flags_inv & HIDEJUMPSUIT))) + if(!w_uniform || (w_uniform && !(w_uniform.body_parts_covered & UPPER_TORSO))) + if(T && T.robotic == ORGAN_ROBOT) + return 1 + + return 0 + #undef HUMAN_EATING_NO_ISSUE #undef HUMAN_EATING_NO_MOUTH #undef HUMAN_EATING_BLOCKED_MOUTH diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm index e87fd83dd4..f65b9926c8 100644 --- a/code/modules/mob/living/carbon/human/human_organs.dm +++ b/code/modules/mob/living/carbon/human/human_organs.dm @@ -9,15 +9,24 @@ /mob/living/carbon/human/var/list/organs_by_name = list() // map organ names to organs /mob/living/carbon/human/var/list/internal_organs_by_name = list() // so internal organs have less ickiness too +/mob/living/carbon/human/proc/get_bodypart_name(var/zone) + var/obj/item/organ/external/E = get_organ(zone) + if(E) . = E.name + +/mob/living/carbon/human/proc/recheck_bad_external_organs() + var/damage_this_tick = getToxLoss() + for(var/obj/item/organ/external/O in organs) + damage_this_tick += O.burn_dam + O.brute_dam + + if(damage_this_tick > last_dam) + . = TRUE + last_dam = damage_this_tick + // Takes care of organ related updates, such as broken and missing limbs /mob/living/carbon/human/proc/handle_organs() - number_wounds = 0 - var/force_process = 0 - var/damage_this_tick = getBruteLoss() + getFireLoss() + getToxLoss() - if(damage_this_tick > last_dam) - force_process = 1 - last_dam = damage_this_tick + var/force_process = recheck_bad_external_organs() + if(force_process) bad_external_organs.Cut() for(var/obj/item/organ/external/Ex in organs) diff --git a/code/modules/mob/living/carbon/human/human_powers.dm b/code/modules/mob/living/carbon/human/human_powers.dm index 48b668a501..948f338297 100644 --- a/code/modules/mob/living/carbon/human/human_powers.dm +++ b/code/modules/mob/living/carbon/human/human_powers.dm @@ -145,4 +145,27 @@ qdel(src) +/mob/living/carbon/human/proc/self_diagnostics() + set name = "Self-Diagnostics" + set desc = "Run an internal self-diagnostic to check for damage." + set category = "IC" + if(stat == DEAD) return + + src << "Performing self-diagnostic, please wait..." + sleep(50) + var/output = "Self-Diagnostic Results:\n" + + for(var/obj/item/organ/external/EO in organs) + if(EO.brute_dam || EO.burn_dam) + output += "[EO.name] - [EO.burn_dam + EO.brute_dam > ROBOLIMB_REPAIR_CAP ? "Heavy Damage" : "Light Damage"]\n" + else + output += "[EO.name] - OK\n" + + for(var/obj/item/organ/IO in internal_organs) + if(IO.damage) + output += "[IO.name] - [IO.damage > 10 ? "Heavy Damage" : "Light Damage"]\n" + else + output += "[IO.name] - OK\n" + + src << output diff --git a/code/modules/mob/living/carbon/human/human_species.dm b/code/modules/mob/living/carbon/human/human_species.dm index 4aad06fd7a..ea1afb157f 100644 --- a/code/modules/mob/living/carbon/human/human_species.dm +++ b/code/modules/mob/living/carbon/human/human_species.dm @@ -2,6 +2,13 @@ real_name = "Test Dummy" status_flags = GODMODE|CANPUSH +/mob/living/carbon/human/dummy/mannequin/New() + ..() + mob_list -= src + living_mob_list -= src + dead_mob_list -= src + delete_inventory() + /mob/living/carbon/human/skrell/New(var/new_loc) h_style = "Skrell Male Tentacles" ..(new_loc, "Skrell") diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 9790559edf..13dbd179c0 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -154,21 +154,6 @@ if(stat != CONSCIOUS) //Let's not worry about tourettes if you're not conscious. return - //Vision - if(species.vision_organ) - if(internal_organs_by_name[species.vision_organ]) // Vision organs cut out? Permablind. - eye_blind = 0 - blinded = 0 - eye_blurry = 0 - else - eye_blind = 1 - blinded = 1 - eye_blurry = 1 - else // Presumably if a species has no vision organs, they see via some other means. - eye_blind = 0 - blinded = 0 - eye_blurry = 0 - if (disabilities & EPILEPSY) if ((prob(1) && paralysis < 1)) src << "\red You have a seizure!" @@ -185,7 +170,6 @@ emote("cough") return if (disabilities & TOURETTES) - speech_problem_flag = 1 if ((prob(10) && paralysis <= 1)) Stun(10) spawn( 0 ) @@ -197,7 +181,6 @@ make_jittery(100) return if (disabilities & NERVOUS) - speech_problem_flag = 1 if (prob(10)) stuttering = max(10, stuttering) @@ -238,7 +221,6 @@ if(!gene.block) continue if(gene.is_active(src)) - speech_problem_flag = 1 gene.OnMobLife(src) radiation = Clamp(radiation,0,100) @@ -931,7 +913,6 @@ adjustHalLoss(-3) if(sleeping) - speech_problem_flag = 1 handle_dreams() if (mind) //Are they SSD? If so we'll keep them asleep but work off some of that sleep var in case of stoxin or similar. @@ -970,19 +951,18 @@ if(species.vision_organ) vision = internal_organs_by_name[species.vision_organ] - if(!vision) // Presumably if a species has no vision organs, they see via some other means. + if(!species.vision_organ) // Presumably if a species has no vision organs, they see via some other means. eye_blind = 0 blinded = 0 eye_blurry = 0 - else if(vision.is_broken()) // Vision organs cut out or broken? Permablind. + else if(!vision || vision.is_broken()) // Vision organs cut out or broken? Permablind. eye_blind = 1 blinded = 1 eye_blurry = 1 - else - //blindness - if(sdisabilities & BLIND) // Disabled-blind, doesn't get better on its own + else //You have the requisite organs + if(sdisabilities & BLIND) // Disabled-blind, doesn't get better on its own blinded = 1 - else if(eye_blind) // Blindness, heals slowly over time + else if(eye_blind) // Blindness, heals slowly over time eye_blind = max(eye_blind-1,0) blinded = 1 else if(istype(glasses, /obj/item/clothing/glasses/sunglasses/blindfold)) //resting your eyes with a blindfold heals blurry eyes faster @@ -1532,7 +1512,7 @@ var/obj/item/organ/internal/heart/H = internal_organs_by_name[O_HEART] - if(!H || (H.status & ORGAN_ROBOT)) + if(!H || (H.robotic >= ORGAN_ROBOT)) return if(pulse >= PULSE_2FAST || shock_stage >= 10 || istype(get_turf(src), /turf/space)) @@ -1584,9 +1564,6 @@ if(stat == DEAD) holder.icon_state = "huddead" holder2.icon_state = "huddead" - else if(status_flags & XENO_HOST) - holder.icon_state = "hudxeno" - holder2.icon_state = "hudxeno" else if(foundVirus) holder.icon_state = "hudill" else if(has_brain_worms()) @@ -1682,28 +1659,11 @@ hud_list[SPECIALROLE_HUD] = holder hud_updateflag = 0 -/mob/living/carbon/human/handle_silent() - if(..()) - speech_problem_flag = 1 - return silent - -/mob/living/carbon/human/handle_slurring() - if(..()) - speech_problem_flag = 1 - return slurring - /mob/living/carbon/human/handle_stunned() if(!can_feel_pain()) stunned = 0 return 0 - if(..()) - speech_problem_flag = 1 - return stunned - -/mob/living/carbon/human/handle_stuttering() - if(..()) - speech_problem_flag = 1 - return stuttering + return ..() /mob/living/carbon/human/handle_fire() if(..()) diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index cbfb88676a..c87338d26c 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -1,10 +1,10 @@ -/mob/living/carbon/human/say(var/message) +/mob/living/carbon/human/say(var/message,var/whispering=0) var/alt_name = "" if(name != GetVoice()) alt_name = "(as [get_id_name("Unknown")])" message = sanitize(message) - ..(message, alt_name = alt_name) + ..(message, alt_name = alt_name, whispering = whispering) /mob/living/carbon/human/proc/forcesay(list/append) if(stat == CONSCIOUS) @@ -125,29 +125,20 @@ return verb -/mob/living/carbon/human/handle_speech_problems(var/message, var/verb) +/mob/living/carbon/human/handle_speech_problems(var/list/message_data) if(silent || (sdisabilities & MUTE)) - message = "" - speech_problem_flag = 1 + message_data[1] = "" + . = 1 + else if(istype(wear_mask, /obj/item/clothing/mask)) var/obj/item/clothing/mask/M = wear_mask if(M.voicechange) - message = pick(M.say_messages) - verb = pick(M.say_verbs) - speech_problem_flag = 1 + message_data[1] = pick(M.say_messages) + message_data[2] = pick(M.say_verbs) + . = 1 - if(message != "") - var/list/parent = ..() - message = parent[1] - verb = parent[2] - if(parent[3]) - speech_problem_flag = 1 - - var/list/returns[3] - returns[1] = message - returns[2] = verb - returns[3] = speech_problem_flag - return returns + else + . = ..(message_data) /mob/living/carbon/human/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name) switch(message_mode) @@ -190,9 +181,6 @@ if(has_radio) R.talk_into(src,message,null,verb,speaking) used_radios += R - if("whisper") - whisper_say(message, speaking, alt_name) - return 1 else if(message_mode) if(l_ear && istype(l_ear,/obj/item/device/radio)) diff --git a/code/modules/mob/living/carbon/human/species/species_getters.dm b/code/modules/mob/living/carbon/human/species/species_getters.dm index 625d555943..efb053eddf 100644 --- a/code/modules/mob/living/carbon/human/species/species_getters.dm +++ b/code/modules/mob/living/carbon/human/species/species_getters.dm @@ -47,10 +47,19 @@ return "no message" /datum/species/proc/get_ssd(var/mob/living/carbon/human/H) - return ((H && H.isSynthetic()) ? "flashing a 'system offline' glyph on their monitor" : show_ssd) + if(H) + if(H.looksSynthetic()) + return "flashing a 'system offline' light" + else + return show_ssd /datum/species/proc/get_blood_colour(var/mob/living/carbon/human/H) - return ((H && H.isSynthetic()) ? SYNTH_BLOOD_COLOUR : blood_color) + if(H) + var/datum/robolimb/company = H.isSynthetic() + if(company) + return company.blood_color + else + return blood_color /datum/species/proc/get_virus_immune(var/mob/living/carbon/human/H) return ((H && H.isSynthetic()) ? 1 : virus_immune) diff --git a/code/modules/mob/living/carbon/human/species/station/station.dm b/code/modules/mob/living/carbon/human/species/station/station.dm index 246b684ca4..a903433fda 100644 --- a/code/modules/mob/living/carbon/human/species/station/station.dm +++ b/code/modules/mob/living/carbon/human/species/station/station.dm @@ -150,7 +150,7 @@ the secrets of their empire to their allies." num_alternate_languages = 2 secondary_langs = list("Skrellian", "Schechi") - name_language = null + name_language = "Skrellian" min_age = 19 max_age = 80 diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 49ae38e789..43123eab0a 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -109,29 +109,30 @@ Please contact me on #coderbus IRC. ~Carn x #define MUTATIONS_LAYER 1 #define DAMAGE_LAYER 2 #define SURGERY_LEVEL 3 //bs12 specific. -#define UNIFORM_LAYER 4 -#define ID_LAYER 5 -#define SHOES_LAYER 6 -#define GLOVES_LAYER 7 -#define BELT_LAYER 8 -#define SUIT_LAYER 9 -#define TAIL_LAYER 10 //bs12 specific. this hack is probably gonna come back to haunt me -#define GLASSES_LAYER 11 -#define BELT_LAYER_ALT 12 -#define SUIT_STORE_LAYER 13 -#define BACK_LAYER 14 -#define HAIR_LAYER 15 //TODO: make part of head layer? -#define EARS_LAYER 16 -#define FACEMASK_LAYER 17 -#define HEAD_LAYER 18 -#define COLLAR_LAYER 19 -#define HANDCUFF_LAYER 20 -#define LEGCUFF_LAYER 21 -#define L_HAND_LAYER 22 -#define R_HAND_LAYER 23 -#define FIRE_LAYER 24 //If you're on fire -#define TARGETED_LAYER 25 //BS12: Layer for the target overlay from weapon targeting system -#define TOTAL_LAYERS 25 +#define UNDERWEAR_LAYER 4 +#define UNIFORM_LAYER 5 +#define ID_LAYER 6 +#define SHOES_LAYER 7 +#define GLOVES_LAYER 8 +#define BELT_LAYER 9 +#define SUIT_LAYER 10 +#define TAIL_LAYER 11 //bs12 specific. this hack is probably gonna come back to haunt me +#define GLASSES_LAYER 12 +#define BELT_LAYER_ALT 13 +#define SUIT_STORE_LAYER 14 +#define BACK_LAYER 15 +#define HAIR_LAYER 16 //TODO: make part of head layer? +#define EARS_LAYER 17 +#define FACEMASK_LAYER 18 +#define HEAD_LAYER 19 +#define COLLAR_LAYER 20 +#define HANDCUFF_LAYER 21 +#define LEGCUFF_LAYER 22 +#define L_HAND_LAYER 23 +#define R_HAND_LAYER 24 +#define FIRE_LAYER 25 //If you're on fire +#define TARGETED_LAYER 26 //BS12: Layer for the target overlay from weapon targeting system +#define TOTAL_LAYERS 26 ////////////////////////////////// /mob/living/carbon/human @@ -148,8 +149,12 @@ Please contact me on #coderbus IRC. ~Carn x if (icon_update) icon = stand_icon - for(var/image/I in overlays_standing) - overlays += I + for(var/entry in overlays_standing) + if(istype(entry, /image)) + overlays += entry + else if(istype(entry, /list)) + for(var/inner_entry in entry) + overlays += inner_entry if(lying && !species.prone_icon) //Only rotate them if we're not drawing a specific icon for being prone. var/matrix/M = matrix() @@ -220,6 +225,8 @@ var/global/list/damage_icon_parts = list() var/hulk = (HULK in src.mutations) var/skeleton = (SKELETON in src.mutations) + robolimb_count = 0 + //CACHING: Generate an index key from visible bodyparts. //0 = destroyed, 1 = normal, 2 = robotic, 3 = necrotic. @@ -247,8 +254,9 @@ var/global/list/damage_icon_parts = list() var/obj/item/organ/external/part = organs_by_name[organ_tag] if(isnull(part) || part.is_stump()) icon_key += "0" - else if(part.status & ORGAN_ROBOT) + else if(part.robotic >= ORGAN_ROBOT) icon_key += "2[part.model ? "-[part.model]": ""]" + robolimb_count++ else if(part.status & ORGAN_DEAD) icon_key += "3" else @@ -315,24 +323,24 @@ var/global/list/damage_icon_parts = list() //END CACHED ICON GENERATION. stand_icon.Blend(base_icon,ICON_OVERLAY) - //Underwear - if(underwear_top && species.appearance_flags & HAS_UNDERWEAR) - stand_icon.Blend(new /icon('icons/mob/human.dmi', underwear_top), ICON_OVERLAY) - if(underwear_bottom && species.appearance_flags & HAS_UNDERWEAR) - stand_icon.Blend(new /icon('icons/mob/human.dmi', underwear_bottom), ICON_OVERLAY) - - if(undershirt && species.appearance_flags & HAS_UNDERWEAR) - stand_icon.Blend(new /icon('icons/mob/human.dmi', undershirt), ICON_OVERLAY) - - if(socks && species.appearance_flags & HAS_UNDERWEAR) - stand_icon.Blend(new /icon('icons/mob/human.dmi', socks), ICON_OVERLAY) - if(update_icons) update_icons() //tail update_tail_showing(0) +//UNDERWEAR OVERLAY +/mob/living/carbon/human/proc/update_underwear(var/update_icons=1) + overlays_standing[UNDERWEAR_LAYER] = null + + if(species.appearance_flags & HAS_UNDERWEAR) + overlays_standing[UNDERWEAR_LAYER] = list() + for(var/category in all_underwear) + var/datum/category_item/underwear/UWI = all_underwear[category] + overlays_standing[UNDERWEAR_LAYER] += UWI.generate_image(all_underwear_metadata[category]) + + if(update_icons) update_icons() + //HAIR OVERLAY /mob/living/carbon/human/proc/update_hair(var/update_icons=1) //Reset our hair @@ -427,6 +435,7 @@ var/global/list/damage_icon_parts = list() update_mutations(0) update_body(0) + update_underwear(0) update_hair(0) update_inv_w_uniform(0) update_inv_wear_id(0) @@ -580,8 +589,9 @@ var/global/list/damage_icon_parts = list() return if(l_ear || r_ear) - var/image/both = image("icon" = null) - + // Blank image upon which to layer left & right overlays. + var/image/both = image("icon" = 'icons/effects/effects.dmi', "icon_state" = "nothing") + if(l_ear) var/image/standing var/t_type = l_ear.icon_state @@ -595,7 +605,7 @@ var/global/list/damage_icon_parts = list() standing = image("icon" = 'icons/mob/ears.dmi', "icon_state" = "[t_type]") standing.color = l_ear.color both.overlays += standing - + if(r_ear) var/image/standing var/t_type = r_ear.icon_state @@ -609,7 +619,7 @@ var/global/list/damage_icon_parts = list() standing = image("icon" = 'icons/mob/ears.dmi', "icon_state" = "[t_type]") standing.color = r_ear.color both.overlays += standing - + overlays_standing[EARS_LAYER] = both else @@ -771,6 +781,12 @@ var/global/list/damage_icon_parts = list() bloodsies.color = wear_suit.blood_color standing.overlays += bloodsies + // Accessories - copied from uniform, BOILERPLATE because fuck this system. + var/obj/item/clothing/suit/suit = wear_suit + if(istype(suit) && suit.accessories.len) + for(var/obj/item/clothing/accessory/A in suit.accessories) + standing.overlays |= A.get_mob_overlay() + overlays_standing[SUIT_LAYER] = standing update_tail_showing(0) diff --git a/code/modules/mob/living/carbon/human/whisper.dm b/code/modules/mob/living/carbon/human/whisper.dm deleted file mode 100644 index 157310291a..0000000000 --- a/code/modules/mob/living/carbon/human/whisper.dm +++ /dev/null @@ -1,161 +0,0 @@ -//Lallander was here -/mob/living/carbon/human/whisper(message as text) - var/alt_name = "" - - if(say_disabled) //This is here to try to identify lag problems - usr << "\red Speech is currently admin-disabled." - return - - message = sanitize(message) - log_whisper("[src.name]/[src.key] : [message]") - - if (src.client) - if (src.client.prefs.muted & MUTE_IC) - src << "\red You cannot whisper (muted)." - return - - if (src.client.handle_spam_prevention(message,MUTE_IC)) - return - - if (src.stat == 2) - return src.say_dead(message) - - if (src.stat) - return - - if(name != GetVoice()) - alt_name = "(as [get_id_name("Unknown")])" - - //parse the language code and consume it - var/datum/language/speaking = parse_language(message) - if (speaking) - message = copytext(message,2+length(speaking.key)) - - whisper_say(message, speaking, alt_name) - - -//This is used by both the whisper verb and human/say() to handle whispering -/mob/living/carbon/human/proc/whisper_say(var/message, var/datum/language/speaking = null, var/alt_name="", var/verb="whispers") - - if (is_muzzled()) - src << "You're muzzled and cannot speak!" - return - - var/message_range = 1 - var/eavesdropping_range = 2 - var/watching_range = 5 - var/italics = 1 - - var/not_heard //the message displayed to people who could not hear the whispering - if (speaking) - if (speaking.whisper_verb) - verb = speaking.whisper_verb - not_heard = "[verb] something" - else - var/adverb = pick("quietly", "softly") - verb = "[speaking.speech_verb] [adverb]" - not_heard = "[speaking.speech_verb] something [adverb]" - else - not_heard = "[verb] something" //TODO get rid of the null language and just prevent speech if language is null - - message = capitalize(trim(message)) - - if(speech_problem_flag) - var/list/handle_r = handle_speech_problems(message) - message = handle_r[1] - verb = handle_r[2] - if(verb == "yells loudly") - verb = "slurs emphatically" - else - var/adverb = pick("quietly", "softly") - verb = "[verb] [adverb]" - - speech_problem_flag = handle_r[3] - - if(!message || message=="") - return - - //looks like this only appears in whisper. Should it be elsewhere as well? Maybe handle_speech_problems? - var/voice_sub - if(istype(back,/obj/item/weapon/rig)) - var/obj/item/weapon/rig/rig = back - // todo: fix this shit - if(rig.speech && rig.speech.voice_holder && rig.speech.voice_holder.active && rig.speech.voice_holder.voice) - voice_sub = rig.speech.voice_holder.voice - else - for(var/obj/item/gear in list(wear_mask,wear_suit,head)) - if(!gear) - continue - var/obj/item/voice_changer/changer = locate() in gear - if(changer && changer.active && changer.voice) - voice_sub = changer.voice - - if(voice_sub == "Unknown") - if(copytext(message, 1, 2) != "*") - var/list/temp_message = splittext(message, " ") - var/list/pick_list = list() - for(var/i = 1, i <= temp_message.len, i++) - pick_list += i - for(var/i=1, i <= abs(temp_message.len/3), i++) - var/H = pick(pick_list) - if(findtext(temp_message[H], "*") || findtext(temp_message[H], ";") || findtext(temp_message[H], ":")) continue - temp_message[H] = ninjaspeak(temp_message[H]) - pick_list -= H - message = jointext(temp_message, " ") - message = replacetext(message, "o", "¤") - message = replacetext(message, "p", "þ") - message = replacetext(message, "l", "£") - message = replacetext(message, "s", "§") - message = replacetext(message, "u", "µ") - message = replacetext(message, "b", "ß") - - var/list/listening = hearers(message_range, src) - listening |= src - - //ghosts - for (var/mob/M in dead_mob_list) //does this include players who joined as observers as well? - if (!(M.client)) - continue - if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) - listening |= M - - //Pass whispers on to anything inside the immediate listeners. - for(var/mob/L in listening) - for(var/mob/C in L.contents) - if(istype(C,/mob/living)) - listening += C - - //pass on the message to objects that can hear us. - for (var/obj/O in view(message_range, src)) - spawn (0) - if (O) - O.hear_talk(src, message, verb, speaking) - - var/list/eavesdropping = hearers(eavesdropping_range, src) - eavesdropping -= src - eavesdropping -= listening - - var/list/watching = hearers(watching_range, src) - watching -= src - watching -= listening - watching -= eavesdropping - - //now mobs - var/speech_bubble_test = say_test(message) - var/image/speech_bubble = image('icons/mob/talk.dmi',src,"h[speech_bubble_test]") - spawn(30) qdel(speech_bubble) - - for(var/mob/M in listening) - M << speech_bubble - M.hear_say(message, verb, speaking, alt_name, italics, src) - - if (eavesdropping.len) - var/new_message = stars(message) //hopefully passing the message twice through stars() won't hurt... I guess if you already don't understand the language, when they speak it too quietly to hear normally you would be able to catch even less. - for(var/mob/M in eavesdropping) - M << speech_bubble - M.hear_say(new_message, verb, speaking, alt_name, italics, src) - - if (watching.len) - var/rendered = "[src.name] [not_heard]." - for (var/mob/M in watching) - M.show_message(rendered, 2) diff --git a/code/modules/mob/living/carbon/metroid/emote.dm b/code/modules/mob/living/carbon/metroid/emote.dm index a6ea5498cd..d82372e52f 100644 --- a/code/modules/mob/living/carbon/metroid/emote.dm +++ b/code/modules/mob/living/carbon/metroid/emote.dm @@ -18,8 +18,6 @@ if (client.prefs.muted & MUTE_IC) src << "\red You cannot send IC messages (muted)." return - if (src.client.handle_spam_prevention(message,MUTE_IC)) - return if (stat) return if(!(message)) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index d9a8ad84b4..111604848a 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -145,7 +145,7 @@ /mob/living/proc/handle_disabilities() //Eyes - if(disabilities & BLIND || stat) //blindness from disability or unconsciousness doesn't get better on its own + if(sdisabilities & BLIND || stat) //blindness from disability or unconsciousness doesn't get better on its own eye_blind = max(eye_blind, 1) else if(eye_blind) //blindness, heals slowly over time eye_blind = max(eye_blind-1,0) @@ -153,7 +153,7 @@ eye_blurry = max(eye_blurry-1, 0) //Ears - if(disabilities & DEAF) //disabled-deaf, doesn't get better on its own + if(sdisabilities & DEAF) //disabled-deaf, doesn't get better on its own setEarDamage(-1, max(ear_deaf, 1)) else // deafness heals slowly over time, unless ear_damage is over 100 diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 3966f60a20..9c37468554 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -144,6 +144,42 @@ O.emp_act(severity) ..() +/mob/living/proc/resolve_item_attack(obj/item/I, mob/living/user, var/target_zone) + return target_zone + +//Called when the mob is hit with an item in combat. Returns the blocked result +/mob/living/proc/hit_with_weapon(obj/item/I, mob/living/user, var/effective_force, var/hit_zone) + visible_message("[src] has been [I.attack_verb.len? pick(I.attack_verb) : "attacked"] with [I.name] by [user]!") + + var/blocked = run_armor_check(hit_zone, "melee") + standard_weapon_hit_effects(I, user, effective_force, blocked, hit_zone) + + if(I.damtype == BRUTE && prob(33)) // Added blood for whacking non-humans too + var/turf/simulated/location = get_turf(src) + if(istype(location)) location.add_blood_floor(src) + + return blocked + +//returns 0 if the effects failed to apply for some reason, 1 otherwise. +/mob/living/proc/standard_weapon_hit_effects(obj/item/I, mob/living/user, var/effective_force, var/blocked, var/hit_zone) + if(!effective_force || blocked >= 100) + return 0 + + //Hulk modifier + if(HULK in user.mutations) + effective_force *= 2 + + //Apply weapon damage + var/weapon_sharp = is_sharp(I) + var/weapon_edge = has_edge(I) + if(prob(max(getarmor(hit_zone, "melee") - I.armor_penetration, 0))) //melee armour provides a chance to turn sharp/edge weapon attacks into blunt ones + weapon_sharp = 0 + weapon_edge = 0 + + apply_damage(effective_force, I.damtype, hit_zone, blocked, sharp=weapon_sharp, edge=weapon_edge, used_weapon=I) + + return 1 + //this proc handles being hit by a thrown atom /mob/living/hitby(atom/movable/AM as mob|obj,var/speed = THROWFORCE_SPEED_DIVISOR)//Standardization and logging -Sieve if(istype(AM,/obj/)) @@ -370,54 +406,3 @@ hud_used.hide_actions_toggle.screen_loc = hud_used.ButtonNumberToScreenCoords(button_number+1) //hud_used.SetButtonCoords(hud_used.hide_actions_toggle,button_number+1) client.screen += hud_used.hide_actions_toggle - - -//If simple_animals are ever moved under carbon, then this can probably be moved to carbon as well -/mob/living/proc/attack_throat(obj/item/W, obj/item/weapon/grab/G, mob/user) - - // Knifing - if(!W.edge || !W.force || W.damtype != BRUTE) return //unsuitable weapon - - user.visible_message("\The [user] begins to slit [src]'s throat with \the [W]!") - - user.next_move = world.time + 20 //also should prevent user from triggering this repeatedly - if(!do_after(user, 20)) - return - if(!(G && G.assailant == user && G.affecting == src)) //check that we still have a grab - return - - var/damage_mod = 1 - //presumably, if they are wearing a helmet that stops pressure effects, then it probably covers the throat as well - var/obj/item/clothing/head/helmet = get_equipped_item(slot_head) - if(istype(helmet) && (helmet.body_parts_covered & HEAD) && (helmet.item_flags & STOPPRESSUREDAMAGE)) - //we don't do an armor_check here because this is not an impact effect like a weapon swung with momentum, that either penetrates or glances off. - damage_mod = 1.0 - (helmet.armor["melee"]/100) - - var/total_damage = 0 - for(var/i in 1 to 3) - var/damage = min(W.force*1.5, 20)*damage_mod - apply_damage(damage, W.damtype, BP_HEAD, 0, sharp=W.sharp, edge=W.edge) - total_damage += damage - - var/oxyloss = total_damage - if(total_damage >= 40) //threshold to make someone pass out - oxyloss = 60 // Brain lacks oxygen immediately, pass out - - adjustOxyLoss(min(oxyloss, 100 - getOxyLoss())) //don't put them over 100 oxyloss - - if(total_damage) - if(oxyloss >= 40) - user.visible_message("\The [user] slit [src]'s throat open with \the [W]!") - else - user.visible_message("\The [user] cut [src]'s neck with \the [W]!") - - if(W.hitsound) - playsound(loc, W.hitsound, 50, 1, -1) - - G.last_action = world.time - flick(G.hud.icon_state, G.hud) - - user.attack_log += "\[[time_stamp()]\] Knifed [name] ([ckey]) with [W.name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(W.damtype)])" - src.attack_log += "\[[time_stamp()]\] Got knifed by [user.name] ([user.ckey]) with [W.name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(W.damtype)])" - msg_admin_attack("[key_name(user)] knifed [key_name(src)] with [W.name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(W.damtype)])" ) - return diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index 54af2697ec..ab26507a2a 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -82,27 +82,31 @@ proc/get_radio_key_from_channel(var/channel) /mob/living/proc/get_default_language() return default_language -/mob/living/proc/handle_speech_problems(var/message, var/verb) - var/list/returns[3] - var/speech_problem_flag = 0 +//Takes a list of the form list(message, verb, whispering) and modifies it as needed +//Returns 1 if a speech problem was applied, 0 otherwise +/mob/living/proc/handle_speech_problems(var/list/message_data) + var/message = message_data[1] + var/verb = message_data[2] + var/whispering = message_data[3] + . = 0 if((HULK in mutations) && health >= 25 && length(message)) message = "[uppertext(message)]!!!" verb = pick("yells","roars","hollers") - speech_problem_flag = 1 + whispering = 0 + . = 1 if(slurring) message = slur(message) verb = pick("slobbers","slurs") - speech_problem_flag = 1 + . = 1 if(stuttering) message = stutter(message) verb = pick("stammers","stutters") - speech_problem_flag = 1 + . = 1 - returns[1] = message - returns[2] = verb - returns[3] = speech_problem_flag - return returns + message_data[1] = message + message_data[2] = verb + message_data[3] = whispering /mob/living/proc/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name) if(message_mode == "intercom") @@ -124,33 +128,42 @@ proc/get_radio_key_from_channel(var/channel) return "asks" return verb -/mob/living/say(var/message, var/datum/language/speaking = null, var/verb="says", var/alt_name="") +/mob/living/say(var/message, var/datum/language/speaking = null, var/verb="says", var/alt_name="", var/whispering = 0) + //If you're muted for IC chat if(client) - if(client.prefs.muted & MUTE_IC) - src << "\red You cannot speak in IC (Muted)." + if((client.prefs.muted & MUTE_IC) || say_disabled) + src << "You cannot speak in IC (Muted)." return + //Redirect to say_dead if talker is dead if(stat) - if(stat == 2) + if(stat == DEAD) return say_dead(message) return + //Parse the mode var/message_mode = parse_message_mode(message, "headset") + //Maybe they are using say/whisper to do a quick emote, so do those switch(copytext(message,1,2)) if("*") return emote(copytext(message,2)) if("^") return custom_emote(1, copytext(message,2)) - //parse the radio code and consume it + //Parse the radio code and consume it if (message_mode) if (message_mode == "headset") message = copytext(message,2) //it would be really nice if the parse procs could do this for us. + else if (message_mode == "whisper") + whispering = 1 + message_mode = null + message = copytext(message,3) else message = copytext(message,3) + //Clean up any remaining space on the left message = trim_left(message) - //parse the language code and consume it + //Parse the language code and consume it if(!speaking) speaking = parse_language(message) if(speaking) @@ -158,42 +171,72 @@ proc/get_radio_key_from_channel(var/channel) else speaking = get_default_language() - // This is broadcast to all mobs with the language, - // irrespective of distance or anything else. + //HIVEMIND languages always send to all people with that language if(speaking && (speaking.flags & HIVEMIND)) speaking.broadcast(src,trim(message)) return 1 - verb = say_quote(message, speaking) - + //Self explanatory. if(is_muzzled()) src << "You're muzzled and cannot speak!" return + //Clean up any remaining junk on the left like spaces. message = trim_left(message) + //Autohiss handles auto-rolling tajaran R's and unathi S's/Z's + message = handle_autohiss(message, speaking) + + //Whisper vars + var/w_scramble_range = 5 //The range at which you get ***as*th**wi**** + var/w_adverb //An adverb prepended to the verb in whispers + var/w_not_heard //The message for people in watching range + + //Handle language-specific verbs and adverb setup if necessary + if(!whispering) //Just doing normal 'say' (for now, may change below) + verb = say_quote(message, speaking) + else if(whispering && speaking.whisper_verb) //Language has defined whisper verb + verb = speaking.whisper_verb + w_not_heard = "[verb] something" + else //Whispering but language has no whisper verb, use say verb + w_adverb = pick("quietly", "softly") + verb = speaking.speech_verb + w_not_heard = "[speaking.speech_verb] something [w_adverb]" + + //For speech disorders (hulk, slurring, stuttering) if(!(speaking && (speaking.flags & NO_STUTTER))) - message = handle_autohiss(message, speaking) + var/list/message_data = list(message, verb, whispering) + if(handle_speech_problems(message_data)) + message = message_data[1] + whispering = message_data[3] - var/list/handle_s = handle_speech_problems(message, verb) - message = handle_s[1] - verb = handle_s[2] + if(verb != message_data[2]) //They changed our verb + if(whispering) + w_adverb = pick("quietly", "softly") + verb = message_data[2] + //Whisper may have adverbs, add those if one was set + if(w_adverb) verb = "[verb] [w_adverb]" + + //If something nulled or emptied the message, forget it if(!message || message == "") return 0 + //Radio message handling var/list/obj/item/used_radios = new - if(handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name)) + if(handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name, whispering)) return 1 + //For languages with actual speech sounds var/list/handle_v = handle_speech_sound() var/sound/speech_sound = handle_v[1] var/sound_vol = handle_v[2] - var/italics = 0 + //Default range and italics, may be overridden past here var/message_range = world.view + var/italics = 0 - //speaking into radios + //Speaking into radios if(used_radios.len) italics = 1 message_range = 1 @@ -208,9 +251,13 @@ proc/get_radio_key_from_channel(var/channel) if (speech_sound) sound_vol *= 0.5 - var/turf/T = get_turf(src) + //Set vars if we're still whispering by this point + if(whispering) + italics = 1 + message_range = 1 + sound_vol *= 0.5 - //handle nonverbal and sign languages here + //Handle nonverbal and sign languages here if (speaking) if (speaking.flags & NONVERBAL) if (prob(30)) @@ -219,55 +266,64 @@ proc/get_radio_key_from_channel(var/channel) if (speaking.flags & SIGNLANG) return say_signlang(message, pick(speaking.signlang_verb), speaking) + //These will contain the main receivers of the message var/list/listening = list() var/list/listening_obj = list() + //Atmosphere calculations (speaker's side only, for now) + var/turf/T = get_turf(src) if(T) - //make sure the air can transmit speech - speaker's side + //Air is too thin to carry sound at all, contact speech only var/datum/gas_mixture/environment = T.return_air() var/pressure = (environment)? environment.return_pressure() : 0 if(pressure < SOUND_MINIMUM_PRESSURE) message_range = 1 - if (pressure < ONE_ATMOSPHERE*0.4) //sound distortion pressure, to help clue people in that the air is thin, even if it isn't a vacuum yet + //Air is nearing minimum levels, make text italics as a hint, and muffle sound + if (pressure < ONE_ATMOSPHERE*0.4) italics = 1 - sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact + sound_vol *= 0.5 - var/list/hear = get_mobs_or_objects_in_view(message_range,src) - var/list/hearturfs = list() - - for(var/I in hear) - if(ismob(I)) - var/mob/M = I - listening += M - hearturfs += M.locs[1] - else if(isobj(I)) - var/obj/O = I - hearturfs += O.locs[1] - listening_obj |= O - - - for(var/mob/M in player_list) - if(M.stat == DEAD && M.client && M.is_preference_enabled(/datum/client_preference/ghost_ears)) - listening |= M - continue - if(M.loc && M.locs[1] in hearturfs) - listening |= M + //Obtain the mobs and objects in the message range + var/list/results = get_mobs_and_objs_in_view_fast(T, world.view) + listening = results["mobs"] + listening_obj = results["objs"] + else + return 1 //If we're in nullspace, then forget it. + //The 'post-say' static speech bubble var/speech_bubble_test = say_test(message) var/image/speech_bubble = image('icons/mob/talk.dmi',src,"h[speech_bubble_test]") spawn(30) qdel(speech_bubble) + //Main 'say' and 'whisper' message delivery for(var/mob/M in listening) - M << speech_bubble - M.hear_say(message, verb, speaking, alt_name, italics, src, speech_sound, sound_vol) + spawn(0) //Using spawns to queue all the messages for AFTER this proc is done, and stop runtimes + if(M && src) //If we still exist, when the spawn processes + var/dst = get_dist(get_turf(M),get_turf(src)) + + if(dst <= message_range || M.stat == DEAD) //Inside normal message range, or dead with ears (handled in the view proc) + M << speech_bubble + M.hear_say(message, verb, speaking, alt_name, italics, src, speech_sound, sound_vol) + + if(whispering) //Don't even bother with these unless whispering + if(dst > message_range && dst <= w_scramble_range) //Inside whisper scramble range + M << speech_bubble + M.hear_say(stars(message), verb, speaking, alt_name, italics, src, speech_sound, sound_vol*0.2) + if(dst > w_scramble_range && dst <= world.view) //Inside whisper 'visible' range + M.show_message("[src.name] [w_not_heard].", 2) + + //Object message delivery for(var/obj/O in listening_obj) spawn(0) - if(O) //It's possible that it could be deleted in the meantime. - O.hear_talk(src, message, verb, speaking) + if(O && src) //If we still exist, when the spawn processes + var/dst = get_dist(get_turf(O),get_turf(src)) + if(dst <= message_range) + O.hear_talk(src, message, verb, speaking) - log_say("[name]/[key] : [message]") + //Log the message to file + log_say("[name]/[key][whispering ? " (W)" : ""]: [message]") return 1 /mob/living/proc/say_signlang(var/message, var/verb="gestures", var/datum/language/language) diff --git a/code/modules/mob/living/silicon/robot/analyzer.dm b/code/modules/mob/living/silicon/robot/analyzer.dm index 22d716aadd..19c44d2e53 100644 --- a/code/modules/mob/living/silicon/robot/analyzer.dm +++ b/code/modules/mob/living/silicon/robot/analyzer.dm @@ -75,7 +75,7 @@ var/organ_found if(H.internal_organs.len) for(var/obj/item/organ/external/E in H.organs) - if(!(E.status & ORGAN_ROBOT)) + if(!(E.robotic >= ORGAN_ROBOT)) continue organ_found = 1 user << "[E.name]: [E.brute_dam] [E.burn_dam]" @@ -86,7 +86,7 @@ organ_found = null if(H.internal_organs.len) for(var/obj/item/organ/O in H.internal_organs) - if(!(O.status & ORGAN_ROBOT)) + if(!(O.robotic >= ORGAN_ROBOT)) continue organ_found = 1 user << "[O.name]: [O.damage]" diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index e91670df5d..3fd496f179 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -63,7 +63,7 @@ /obj/item/borg/upgrade, /obj/item/device/flash, //to build borgs, /obj/item/organ/internal/brain, //to insert into MMIs, - /obj/item/stack/cable_coil, //again, for borg building, + /obj/item/weapon/disk, /obj/item/weapon/circuitboard, /obj/item/slime_extract, /obj/item/weapon/reagent_containers/glass, @@ -83,6 +83,35 @@ /obj/item/weapon/grown ) +/obj/item/weapon/gripper/no_use/organ + name = "organ gripper" + icon_state = "gripper-flesh" + desc = "A specialized grasping tool used to preserve and manipulate organic material." + + can_hold = list( + /obj/item/organ + ) + +/obj/item/weapon/gripper/no_use/organ/robotics + name = "external organ gripper" + icon_state = "gripper-flesh" + desc = "A specialized grasping tool used in robotics work." + + can_hold = list( + /obj/item/organ/external, + /obj/item/organ/internal/cell + ) + +/obj/item/weapon/gripper/no_use/mech + name = "exosuit gripper" + icon_state = "gripper-mech" + desc = "A large, heavy-duty grasping tool used in construction of mechs." + + can_hold = list( + /obj/item/mecha_parts/part, + /obj/item/mecha_parts/mecha_equipment + ) + /obj/item/weapon/gripper/no_use //Used when you want to hold and put items in other things, but not able to 'use' the item /obj/item/weapon/gripper/no_use/attack_self(mob/user as mob) diff --git a/code/modules/mob/living/silicon/robot/drone/drone_say.dm b/code/modules/mob/living/silicon/robot/drone/drone_say.dm index b0f89a70ea..5cdcd3dbd8 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_say.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_say.dm @@ -4,8 +4,6 @@ if(client.prefs.muted & MUTE_IC) src << "You cannot send IC messages (muted)." return 0 - if (src.client.handle_spam_prevention(message,MUTE_IC)) - return 0 message = sanitize(message) diff --git a/code/modules/mob/living/silicon/robot/emote.dm b/code/modules/mob/living/silicon/robot/emote.dm index 23b6cc0d67..7f2102a860 100644 --- a/code/modules/mob/living/silicon/robot/emote.dm +++ b/code/modules/mob/living/silicon/robot/emote.dm @@ -14,8 +14,6 @@ if(client.prefs.muted & MUTE_IC) src << "You cannot send IC messages (muted)." return - if (src.client.handle_spam_prevention(message,MUTE_IC)) - return if (stat) return if(!(message)) diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index e6c7546e09..cd02dd71f4 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -187,7 +187,7 @@ var/global/list/robot_modules = list( /obj/item/weapon/robot_module/medical/surgeon/New() src.modules += new /obj/item/device/flash(src) src.modules += new /obj/item/device/healthanalyzer(src) - src.modules += new /obj/item/weapon/reagent_containers/borghypo/surgeon(src) + src.modules += new /obj/item/weapon/reagent_containers/borghypo(src) src.modules += new /obj/item/weapon/scalpel(src) src.modules += new /obj/item/weapon/hemostat(src) src.modules += new /obj/item/weapon/retractor(src) @@ -197,6 +197,7 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/weapon/bonesetter(src) src.modules += new /obj/item/weapon/circular_saw(src) src.modules += new /obj/item/weapon/surgicaldrill(src) + src.modules += new /obj/item/weapon/gripper/no_use/organ(src) src.modules += new /obj/item/weapon/extinguisher/mini(src) src.emag = new /obj/item/weapon/reagent_containers/spray(src) src.emag.reagents.add_reagent("pacid", 250) @@ -241,10 +242,11 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/device/healthanalyzer(src) src.modules += new /obj/item/device/reagent_scanner/adv(src) src.modules += new /obj/item/roller_holder(src) - src.modules += new /obj/item/weapon/reagent_containers/borghypo/crisis(src) + src.modules += new /obj/item/weapon/reagent_containers/borghypo(src) src.modules += new /obj/item/weapon/reagent_containers/glass/beaker/large(src) src.modules += new /obj/item/weapon/reagent_containers/dropper/industrial(src) src.modules += new /obj/item/weapon/reagent_containers/syringe(src) + src.modules += new /obj/item/weapon/gripper/no_use/organ(src) src.modules += new /obj/item/weapon/extinguisher/mini(src) src.emag = new /obj/item/weapon/reagent_containers/spray(src) src.emag.reagents.add_reagent("pacid", 250) @@ -312,6 +314,7 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/weapon/screwdriver(src) src.modules += new /obj/item/weapon/wrench(src) src.modules += new /obj/item/weapon/crowbar(src) + src.modules += new /obj/item/weapon/weldingtool/largetank(src) src.modules += new /obj/item/weapon/pickaxe/plasmacutter(src) src.modules += new /obj/item/device/pipe_painter(src) src.modules += new /obj/item/device/floor_painter(src) @@ -332,6 +335,10 @@ var/global/list/robot_modules = list( R.synths = list(metal) src.modules += R + var/obj/item/stack/tile/floor/cyborg/F = new /obj/item/stack/tile/floor/cyborg(src) + F.synths = list(metal) + src.modules += F + var/obj/item/stack/material/cyborg/plasteel/S = new (src) S.synths = list(plasteel) src.modules += S @@ -358,6 +365,7 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/weapon/gripper(src) src.modules += new /obj/item/device/lightreplacer(src) src.modules += new /obj/item/device/pipe_painter(src) + src.modules += new /obj/item/device/floor_painter(src) src.emag = new /obj/item/borg/stun(src) var/datum/matter_synth/metal = new /datum/matter_synth/metal(40000) @@ -423,6 +431,7 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/weapon/melee/baton/robot(src) src.modules += new /obj/item/weapon/gun/energy/taser/mounted/cyborg(src) src.modules += new /obj/item/taperoll/police(src) + src.modules += new /obj/item/weapon/reagent_containers/spray/pepper(src) src.emag = new /obj/item/weapon/gun/energy/laser/mounted(src) ..() @@ -506,6 +515,8 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/device/analyzer/plant_analyzer(src) src.modules += new /obj/item/weapon/storage/bag/plants(src) src.modules += new /obj/item/weapon/robot_harvester(src) + src.modules += new /obj/item/weapon/material/knife(src) + src.modules += new /obj/item/weapon/material/kitchen/rollingpin(src) var/obj/item/weapon/rsf/M = new /obj/item/weapon/rsf(src) M.stored_matter = 30 @@ -545,8 +556,10 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/weapon/form_printer(src) src.modules += new /obj/item/weapon/gripper/paperwork(src) src.modules += new /obj/item/weapon/hand_labeler(src) - src.emag = new /obj/item/weapon/stamp/denied(src) - ..() + src.modules += new /obj/item/weapon/stamp(src) + src.modules += new /obj/item/weapon/stamp/denied(src) + src.emag = new /obj/item/weapon/stamp/chameleon(src) + src.emag = new /obj/item/weapon/pen/chameleon(src) /obj/item/weapon/robot_module/general/butler/respawn_consumable(var/mob/living/silicon/robot/R, var/amount) var/obj/item/weapon/reagent_containers/food/condiment/enzyme/E = locate() in src.modules @@ -593,12 +606,17 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/device/flash(src) src.modules += new /obj/item/weapon/portable_destructive_analyzer(src) src.modules += new /obj/item/weapon/gripper/research(src) + src.modules += new /obj/item/weapon/gripper/no_use/organ/robotics(src) + src.modules += new /obj/item/weapon/gripper/no_use/mech(src) src.modules += new /obj/item/weapon/gripper/no_use/loader(src) src.modules += new /obj/item/device/robotanalyzer(src) src.modules += new /obj/item/weapon/card/robot(src) - src.modules += new /obj/item/weapon/wrench(src) + src.modules += new /obj/item/weapon/weldingtool/largetank(src) src.modules += new /obj/item/weapon/screwdriver(src) + src.modules += new /obj/item/weapon/wrench(src) src.modules += new /obj/item/weapon/crowbar(src) + src.modules += new /obj/item/weapon/wirecutters(src) + src.modules += new /obj/item/device/multitool(src) src.modules += new /obj/item/weapon/scalpel(src) src.modules += new /obj/item/weapon/circular_saw(src) src.modules += new /obj/item/weapon/extinguisher/mini(src) @@ -608,6 +626,8 @@ var/global/list/robot_modules = list( var/datum/matter_synth/nanite = new /datum/matter_synth/nanite(10000) synths += nanite + var/datum/matter_synth/wire = new /datum/matter_synth/wire() //Added to allow repairs, would rather add cable now than be asked to add it later, + synths += wire //Cable code, taken from engiborg, var/obj/item/stack/nanopaste/N = new /obj/item/stack/nanopaste(src) N.uses_charge = 1 @@ -615,6 +635,10 @@ var/global/list/robot_modules = list( N.synths = list(nanite) src.modules += N + var/obj/item/stack/cable_coil/cyborg/C = new /obj/item/stack/cable_coil/cyborg(src) //Cable code, taken from engiborg, + C.synths = list(wire) + src.modules += C + ..() /obj/item/weapon/robot_module/syndicate diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm index ece00da5ee..e2810ad641 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -1,5 +1,5 @@ -/mob/living/silicon/say(var/message, var/sanitize = 1) - return ..(sanitize ? sanitize(message) : message) +/mob/living/silicon/say(var/message, var/sanitize = 1, var/whispering = 0) + return ..((sanitize ? sanitize(message) : message), whispering = whispering) /mob/living/silicon/handle_message_mode(message_mode, message, verb, speaking, used_radios, alt_name) log_say("[key_name(src)] : [message]") diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 0d0a83b7d1..d670e17ee3 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -132,9 +132,6 @@ updatehealth() return 1*/ -/mob/living/silicon/attack_throat() - return - /proc/islinked(var/mob/living/silicon/robot/bot, var/mob/living/silicon/ai/ai) if(!istype(bot) || !istype(ai)) return 0 diff --git a/code/modules/mob/living/simple_animal/borer/borer_captive.dm b/code/modules/mob/living/simple_animal/borer/borer_captive.dm index 2d6bfc34b3..f07b9d32ce 100644 --- a/code/modules/mob/living/simple_animal/borer/borer_captive.dm +++ b/code/modules/mob/living/simple_animal/borer/borer_captive.dm @@ -9,8 +9,6 @@ if(client.prefs.muted & MUTE_IC) src << "\red You cannot speak in IC (muted)." return - if (src.client.handle_spam_prevention(message,MUTE_IC)) - return if(istype(src.loc,/mob/living/simple_animal/borer)) diff --git a/code/modules/mob/living/simple_animal/borer/say.dm b/code/modules/mob/living/simple_animal/borer/say.dm index 0d4cdad06e..e76a02a7e2 100644 --- a/code/modules/mob/living/simple_animal/borer/say.dm +++ b/code/modules/mob/living/simple_animal/borer/say.dm @@ -16,8 +16,6 @@ if(client.prefs.muted & MUTE_IC) src << "\red You cannot speak in IC (muted)." return - if (src.client.handle_spam_prevention(message,MUTE_IC)) - return if (copytext(message, 1, 2) == "*") return emote(copytext(message, 2)) diff --git a/code/modules/mob/living/simple_animal/friendly/spiderbot.dm b/code/modules/mob/living/simple_animal/friendly/spiderbot.dm index db6eb8f4b7..394dd742ac 100644 --- a/code/modules/mob/living/simple_animal/friendly/spiderbot.dm +++ b/code/modules/mob/living/simple_animal/friendly/spiderbot.dm @@ -130,7 +130,7 @@ return 0 else - attacked_with_item(O, user) + O.attack(src, user, user.zone_sel.selecting) /mob/living/simple_animal/spiderbot/emag_act(var/remaining_charges, var/mob/user) if (emagged) diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm index 91f8aa958c..68a7f8cbd3 100644 --- a/code/modules/mob/living/simple_animal/hostile/bear.dm +++ b/code/modules/mob/living/simple_animal/hostile/bear.dm @@ -57,16 +57,16 @@ switch(stance) - if(HOSTILE_STANCE_TIRED) + if(STANCE_TIRED) stop_automated_movement = 1 stance_step++ if(stance_step >= 10) //rests for 10 ticks if(target_mob && target_mob in ListTargets(10)) - stance = HOSTILE_STANCE_ATTACK //If the mob he was chasing is still nearby, resume the attack, otherwise go idle. + stance = STANCE_ATTACK //If the mob he was chasing is still nearby, resume the attack, otherwise go idle. else - stance = HOSTILE_STANCE_IDLE + stance = STANCE_IDLE - if(HOSTILE_STANCE_ALERT) + if(STANCE_ALERT) stop_automated_movement = 1 var/found_mob = 0 if(target_mob && target_mob in ListTargets(10)) @@ -84,14 +84,14 @@ stance_step-- if(stance_step <= -20) //If we have not found a mob for 20-ish ticks, revert to idle mode - stance = HOSTILE_STANCE_IDLE + stance = STANCE_IDLE if(stance_step >= 7) //If we have been staring at a mob for 7 ticks, - stance = HOSTILE_STANCE_ATTACK + stance = STANCE_ATTACK - if(HOSTILE_STANCE_ATTACKING) + if(STANCE_ATTACKING) if(stance_step >= 20) //attacks for 20 ticks, then it gets tired and needs to rest custom_emote(1, "is worn out and needs to rest." ) - stance = HOSTILE_STANCE_TIRED + stance = STANCE_TIRED stance_step = 0 walk(src, 0) //This stops the bear's walking return @@ -99,15 +99,15 @@ /mob/living/simple_animal/hostile/bear/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(stance != HOSTILE_STANCE_ATTACK && stance != HOSTILE_STANCE_ATTACKING) - stance = HOSTILE_STANCE_ALERT + if(stance != STANCE_ATTACK && stance != STANCE_ATTACKING) + stance = STANCE_ALERT stance_step = 6 target_mob = user ..() /mob/living/simple_animal/hostile/bear/attack_hand(mob/living/carbon/human/M as mob) - if(stance != HOSTILE_STANCE_ATTACK && stance != HOSTILE_STANCE_ATTACKING) - stance = HOSTILE_STANCE_ALERT + if(stance != STANCE_ATTACK && stance != STANCE_ATTACKING) + stance = STANCE_ALERT stance_step = 6 target_mob = M ..() @@ -119,7 +119,7 @@ . = ..() if(.) custom_emote(1,"stares alertly at [.]") - stance = HOSTILE_STANCE_ALERT + stance = STANCE_ALERT /mob/living/simple_animal/hostile/bear/LoseTarget() ..(5) diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm index bf4ea6b572..262aad8e1a 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm @@ -83,14 +83,14 @@ var/mob/living/carbon/human/H = target if(prob(poison_per_bite)) var/obj/item/organ/external/O = pick(H.organs) - if(!(O.status & ORGAN_ROBOT)) + if(!(O.robotic >= ORGAN_ROBOT)) var/eggs = PoolOrNew(/obj/effect/spider/eggcluster/, list(O, src)) O.implants += eggs /mob/living/simple_animal/hostile/giant_spider/Life() ..() if(!stat) - if(stance == HOSTILE_STANCE_IDLE) + if(stance == STANCE_IDLE) //1% chance to skitter madly away if(!busy && prob(1)) /*var/list/move_targets = list() @@ -113,7 +113,7 @@ /mob/living/simple_animal/hostile/giant_spider/nurse/Life() ..() if(!stat) - if(stance == HOSTILE_STANCE_IDLE) + if(stance == STANCE_IDLE) var/list/can_see = view(src, 10) //30% chance to stop wandering and do something if(!busy && prob(30)) @@ -213,4 +213,4 @@ #undef SPINNING_WEB #undef LAYING_EGGS #undef MOVING_TO_TARGET -#undef SPINNING_COCOON +#undef SPINNING_COCOON diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index 33fb8acd24..c4dcfbd3f1 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -1,234 +1,15 @@ /mob/living/simple_animal/hostile faction = "hostile" - var/stance = HOSTILE_STANCE_IDLE //Used to determine behavior - var/mob/living/target_mob - var/attack_same = 0 - var/ranged = 0 - var/rapid = 0 - var/projectiletype - var/projectilesound - var/casingtype - var/move_to_delay = 4 //delay for the automated movement. - var/list/friends = list() - var/break_stuff_probability = 10 + break_stuff_probability = 10 stop_automated_movement_when_pulled = 0 - var/destroy_surroundings = 1 + destroy_surroundings = 1 a_intent = I_HURT + hostile = 1 - var/shuttletarget = null - var/enroute = 0 - -/mob/living/simple_animal/hostile/proc/FindTarget() - - var/atom/T = null - stop_automated_movement = 0 - for(var/atom/A in ListTargets(10)) - - if(A == src) - continue - - var/atom/F = Found(A) - if(F) - T = F - break - - if(isliving(A)) - var/mob/living/L = A - if(L.faction == src.faction && !attack_same) - continue - else if(L in friends) - continue - else - if(!L.stat) - stance = HOSTILE_STANCE_ATTACK - T = L - break - - else if(istype(A, /obj/mecha)) // Our line of sight stuff was already done in ListTargets(). - var/obj/mecha/M = A - if (M.occupant) - stance = HOSTILE_STANCE_ATTACK - T = M - break - return T - - -/mob/living/simple_animal/hostile/proc/Found(var/atom/A) - return - -/mob/living/simple_animal/hostile/proc/MoveToTarget() - stop_automated_movement = 1 - if(!target_mob || SA_attackable(target_mob)) - stance = HOSTILE_STANCE_IDLE - if(target_mob in ListTargets(10)) - if(ranged) - if(get_dist(src, target_mob) <= 6) - OpenFire(target_mob) - else - walk_to(src, target_mob, 1, move_to_delay) - else - stance = HOSTILE_STANCE_ATTACKING - walk_to(src, target_mob, 1, move_to_delay) - -/mob/living/simple_animal/hostile/proc/AttackTarget() - - stop_automated_movement = 1 - if(!target_mob || SA_attackable(target_mob)) - LoseTarget() - return 0 - if(!(target_mob in ListTargets(10))) - LostTarget() - return 0 - if(get_dist(src, target_mob) <= 1) //Attacking - AttackingTarget() - return 1 - -/mob/living/simple_animal/hostile/proc/AttackingTarget() - if(!Adjacent(target_mob)) - return - if(isliving(target_mob)) - var/mob/living/L = target_mob - L.attack_generic(src,rand(melee_damage_lower,melee_damage_upper),attacktext) - return L - if(istype(target_mob,/obj/mecha)) - var/obj/mecha/M = target_mob - M.attack_generic(src,rand(melee_damage_lower,melee_damage_upper),attacktext) - return M - -/mob/living/simple_animal/hostile/proc/LoseTarget() - stance = HOSTILE_STANCE_IDLE - target_mob = null - walk(src, 0) - -/mob/living/simple_animal/hostile/proc/LostTarget() - stance = HOSTILE_STANCE_IDLE - walk(src, 0) - - -/mob/living/simple_animal/hostile/proc/ListTargets(var/dist = 7) - var/list/L = hearers(src, dist) - - for (var/obj/mecha/M in mechas_list) - if (M.z == src.z && get_dist(src, M) <= dist) - L += M - - return L - -/mob/living/simple_animal/hostile/death() - ..() - walk(src, 0) /mob/living/simple_animal/hostile/Life() . = ..() if(!.) walk(src, 0) - return 0 - if(client) - return 0 - - if(!stat) - switch(stance) - if(HOSTILE_STANCE_IDLE) - target_mob = FindTarget() - - if(HOSTILE_STANCE_ATTACK) - if(destroy_surroundings) - DestroySurroundings() - MoveToTarget() - - if(HOSTILE_STANCE_ATTACKING) - if(destroy_surroundings) - DestroySurroundings() - AttackTarget() - -/mob/living/simple_animal/hostile/proc/OpenFire(target_mob) - var/target = target_mob - visible_message("\red [src] fires at [target]!", 1) - - var/tturf = get_turf(target) - if(rapid) - spawn(1) - Shoot(tturf, src.loc, src) - if(casingtype) - new casingtype(get_turf(src)) - spawn(4) - Shoot(tturf, src.loc, src) - if(casingtype) - new casingtype(get_turf(src)) - spawn(6) - Shoot(tturf, src.loc, src) - if(casingtype) - new casingtype(get_turf(src)) - else - Shoot(tturf, src.loc, src) - if(casingtype) - new casingtype - - stance = HOSTILE_STANCE_IDLE - target_mob = null - return - - -/mob/living/simple_animal/hostile/proc/Shoot(var/target, var/start, var/user, var/bullet = 0) - if(target == start) - return - - var/obj/item/projectile/A = new projectiletype(user:loc) - playsound(user, projectilesound, 100, 1) - if(!A) return - - if (!istype(target, /turf)) - qdel(A) - return - A.launch(target) - return - -/mob/living/simple_animal/hostile/proc/DestroySurroundings() - if(prob(break_stuff_probability)) - for(var/dir in cardinal) // North, South, East, West - for(var/obj/structure/window/obstacle in get_step(src, dir)) - if(obstacle.dir == reverse_dir[dir]) // So that windows get smashed in the right order - obstacle.attack_generic(src,rand(melee_damage_lower,melee_damage_upper),attacktext) - return - var/obj/structure/obstacle = locate(/obj/structure, get_step(src, dir)) - if(istype(obstacle, /obj/structure/window) || istype(obstacle, /obj/structure/closet) || istype(obstacle, /obj/structure/table) || istype(obstacle, /obj/structure/grille)) - obstacle.attack_generic(src,rand(melee_damage_lower,melee_damage_upper),attacktext) - - -/mob/living/simple_animal/hostile/proc/check_horde() - return 0 - if(emergency_shuttle.shuttle.location) - if(!enroute && !target_mob) //The shuttle docked, all monsters rush for the escape hallway - if(!shuttletarget && escape_list.len) //Make sure we didn't already assign it a target, and that there are targets to pick - shuttletarget = pick(escape_list) //Pick a shuttle target - enroute = 1 - stop_automated_movement = 1 - spawn() - if(!src.stat) - horde() - - if(get_dist(src, shuttletarget) <= 2) //The monster reached the escape hallway - enroute = 0 - stop_automated_movement = 0 - -/mob/living/simple_animal/hostile/proc/horde() - var/turf/T = get_step_to(src, shuttletarget) - for(var/atom/A in T) - if(istype(A,/obj/machinery/door/airlock)) - var/obj/machinery/door/airlock/D = A - D.open(1) - else if(istype(A,/obj/structure/simple_door)) - var/obj/structure/simple_door/D = A - if(D.density) - D.Open() - else if(istype(A,/obj/structure/cult/pylon)) - A.attack_generic(src, rand(melee_damage_lower, melee_damage_upper)) - else if(istype(A, /obj/structure/window) || istype(A, /obj/structure/closet) || istype(A, /obj/structure/table) || istype(A, /obj/structure/grille)) - A.attack_generic(src, rand(melee_damage_lower, melee_damage_upper)) - Move(T) - FindTarget() - if(!target_mob || enroute) - spawn(10) - if(!src.stat) - horde() + return 0 \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm index 49a3055859..ac352edd1a 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm @@ -5,14 +5,14 @@ if(isliving(A)) var/mob/living/L = A if(!L.stat) - stance = HOSTILE_STANCE_ATTACK + stance = STANCE_ATTACK return L else enemies -= L else if(istype(A, /obj/mecha)) var/obj/mecha/M = A if(M.occupant) - stance = HOSTILE_STANCE_ATTACK + stance = STANCE_ATTACK return A /mob/living/simple_animal/hostile/retaliate/ListTargets() diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 2fbad9deee..08ff09703f 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -66,6 +66,33 @@ var/supernatural = 0 var/purge = 0 + //Pulling hostile mob vars down + var/stance = STANCE_IDLE //Used to determine behavior + var/mob/living/target_mob + var/attack_same = 0 + var/ranged = 0 + var/rapid = 0 + var/projectiletype + var/projectilesound + var/casingtype + var/move_to_delay = 4 //delay for the automated movement. + var/list/friends = list() + var/break_stuff_probability = 0 + var/destroy_surroundings = 0 + var/shuttletarget = null + var/enroute = 0 + + var/list/resistances = list( + HALLOSS = 0, + BRUTE = 1, + BURN = 1, + TOX = 1, + OXY = 0, + CLONE = 0 + ) + + var/hostile = 0 + /mob/living/simple_animal/New() ..() verbs -= /mob/verb/observe @@ -204,6 +231,23 @@ if(!atmos_suitable) adjustBruteLoss(unsuitable_atoms_damage) + + //Hostility + if(!stat && !client && hostile) + switch(stance) + if(STANCE_IDLE) + target_mob = FindTarget() + + if(STANCE_ATTACK) + if(destroy_surroundings) + DestroySurroundings() + MoveToTarget() + + if(STANCE_ATTACKING) + if(destroy_surroundings) + DestroySurroundings() + AttackTarget() + return 1 /mob/living/simple_animal/proc/handle_supernatural() @@ -287,28 +331,28 @@ if(istype(O, /obj/item/weapon/material/knife) || istype(O, /obj/item/weapon/material/knife/butch)) harvest(user) else - attacked_with_item(O, user) + if(!O.force) + visible_message("[user] gently taps [src] with \the [O].") + else + O.attack(src, user, user.zone_sel.selecting) -//TODO: refactor mob attackby(), attacked_by(), and friends. -/mob/living/simple_animal/proc/attacked_with_item(var/obj/item/O, var/mob/user) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - if(!O.force) - visible_message("[user] gently taps [src] with \the [O].") - return - - if(O.force > resistance) - var/damage = O.force - if (O.damtype == HALLOSS) - damage = 0 - if(supernatural && istype(O,/obj/item/weapon/nullrod)) - damage *= 2 - purge = 3 - adjustBruteLoss(damage) - else - usr << "This weapon is ineffective, it does no damage." +/mob/living/simple_animal/hit_with_weapon(obj/item/O, mob/living/user, var/effective_force, var/hit_zone) visible_message("\The [src] has been attacked with \the [O] by [user].") - user.do_attack_animation(src) + + if(O.force <= resistance) + user << "This weapon is ineffective, it does no damage." + return 2 + + var/damage = O.force + if (O.damtype == HALLOSS) + damage = 0 + if(supernatural && istype(O,/obj/item/weapon/nullrod)) + damage *= 2 + purge = 3 + adjustBruteLoss(damage) + + return 0 /mob/living/simple_animal/movement_delay() var/tally = 0 //Incase I need to add stuff other than "speed" later @@ -402,3 +446,191 @@ return /mob/living/simple_animal/ExtinguishMob() return + + //Hostile procs moved down +/mob/living/simple_animal/proc/FindTarget() + + var/atom/T = null + stop_automated_movement = 0 + for(var/atom/A in ListTargets(10)) + + if(A == src) + continue + + var/atom/F = Found(A) + if(F) + T = F + break + + if(isliving(A)) + var/mob/living/L = A + if(L.faction == src.faction && !attack_same) + continue + else if(L in friends) + continue + else + if(!L.stat) + stance = STANCE_ATTACK + T = L + break + + else if(istype(A, /obj/mecha)) // Our line of sight stuff was already done in ListTargets(). + var/obj/mecha/M = A + if (M.occupant) + stance = STANCE_ATTACK + T = M + break + return T + + +/mob/living/simple_animal/proc/Found(var/atom/A) + return + +/mob/living/simple_animal/proc/MoveToTarget() + stop_automated_movement = 1 + if(!target_mob || SA_attackable(target_mob)) + stance = STANCE_IDLE + if(target_mob in ListTargets(10)) + if(ranged) + if(get_dist(src, target_mob) <= 6) + OpenFire(target_mob) + else + walk_to(src, target_mob, 1, move_to_delay) + else + stance = STANCE_ATTACKING + walk_to(src, target_mob, 1, move_to_delay) + +/mob/living/simple_animal/proc/AttackTarget() + + stop_automated_movement = 1 + if(!target_mob || SA_attackable(target_mob)) + LoseTarget() + return 0 + if(!(target_mob in ListTargets(10))) + LostTarget() + return 0 + if(get_dist(src, target_mob) <= 1) //Attacking + AttackingTarget() + return 1 + +/mob/living/simple_animal/proc/AttackingTarget() + if(!Adjacent(target_mob)) + return + if(isliving(target_mob)) + var/mob/living/L = target_mob + L.attack_generic(src,rand(melee_damage_lower,melee_damage_upper),attacktext) + return L + if(istype(target_mob,/obj/mecha)) + var/obj/mecha/M = target_mob + M.attack_generic(src,rand(melee_damage_lower,melee_damage_upper),attacktext) + return M + +/mob/living/simple_animal/proc/LoseTarget() + stance = STANCE_IDLE + target_mob = null + walk(src, 0) + +/mob/living/simple_animal/proc/LostTarget() + stance = STANCE_IDLE + walk(src, 0) + + +/mob/living/simple_animal/proc/ListTargets(var/dist = 7) + var/list/L = hearers(src, dist) + + for (var/obj/mecha/M in mechas_list) + if (M.z == src.z && get_dist(src, M) <= dist) + L += M + + return L + +/mob/living/simple_animal/proc/OpenFire(target_mob) + var/target = target_mob + visible_message("\red [src] fires at [target]!", 1) + + var/tturf = get_turf(target) + if(rapid) + spawn(1) + Shoot(tturf, src.loc, src) + if(casingtype) + new casingtype(get_turf(src)) + spawn(4) + Shoot(tturf, src.loc, src) + if(casingtype) + new casingtype(get_turf(src)) + spawn(6) + Shoot(tturf, src.loc, src) + if(casingtype) + new casingtype(get_turf(src)) + else + Shoot(tturf, src.loc, src) + if(casingtype) + new casingtype + + stance = STANCE_IDLE + target_mob = null + return + + +/mob/living/simple_animal/proc/Shoot(var/target, var/start, var/user, var/bullet = 0) + if(target == start) + return + + var/obj/item/projectile/A = new projectiletype(user:loc) + playsound(user, projectilesound, 100, 1) + if(!A) return + + if (!istype(target, /turf)) + qdel(A) + return + A.launch(target) + return + +/mob/living/simple_animal/proc/DestroySurroundings() + if(prob(break_stuff_probability)) + for(var/dir in cardinal) // North, South, East, West + for(var/obj/structure/window/obstacle in get_step(src, dir)) + if(obstacle.dir == reverse_dir[dir]) // So that windows get smashed in the right order + obstacle.attack_generic(src,rand(melee_damage_lower,melee_damage_upper),attacktext) + return + var/obj/structure/obstacle = locate(/obj/structure, get_step(src, dir)) + if(istype(obstacle, /obj/structure/window) || istype(obstacle, /obj/structure/closet) || istype(obstacle, /obj/structure/table) || istype(obstacle, /obj/structure/grille)) + obstacle.attack_generic(src,rand(melee_damage_lower,melee_damage_upper),attacktext) + + +/mob/living/simple_animal/proc/check_horde() + return 0 + if(emergency_shuttle.shuttle.location) + if(!enroute && !target_mob) //The shuttle docked, all monsters rush for the escape hallway + if(!shuttletarget && escape_list.len) //Make sure we didn't already assign it a target, and that there are targets to pick + shuttletarget = pick(escape_list) //Pick a shuttle target + enroute = 1 + stop_automated_movement = 1 + spawn() + if(!src.stat) + horde() + + if(get_dist(src, shuttletarget) <= 2) //The monster reached the escape hallway + enroute = 0 + stop_automated_movement = 0 + +/mob/living/simple_animal/proc/horde() + var/turf/T = get_step_to(src, shuttletarget) + for(var/atom/A in T) + if(istype(A,/obj/machinery/door/airlock)) + var/obj/machinery/door/airlock/D = A + D.open(1) + else if(istype(A,/obj/structure/simple_door)) + var/obj/structure/simple_door/D = A + if(D.density) + D.Open() + else if(istype(A,/obj/structure/cult/pylon)) + A.attack_generic(src, rand(melee_damage_lower, melee_damage_upper)) + else if(istype(A, /obj/structure/window) || istype(A, /obj/structure/closet) || istype(A, /obj/structure/table) || istype(A, /obj/structure/grille)) + A.attack_generic(src, rand(melee_damage_lower, melee_damage_upper)) + Move(T) + FindTarget() + if(!target_mob || enroute) + spawn(10) + if(!src.stat) + horde() \ No newline at end of file diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 51f223295e..5271034d2b 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -19,18 +19,6 @@ return L.mob_size <= MOB_MINISCULE return 0 - -// If they are 100% robotic, they count as synthetic. -/mob/living/carbon/human/isSynthetic() - if(isnull(full_prosthetic)) - robolimb_count = 0 - for(var/obj/item/organ/external/E in organs) - if(E.status & ORGAN_ROBOT) - robolimb_count++ - if(robolimb_count == organs.len) - full_prosthetic = 1 - return full_prosthetic - /mob/living/silicon/isSynthetic() return 1 diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 55530ddcd8..a1b8437b0b 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -127,9 +127,13 @@ observer.timeofdeath = world.time // Set the time of death so that the respawn timer works correctly. announce_ghost_joinleave(src) - client.prefs.update_preview_icon() - observer.icon = client.prefs.preview_icon + var/mob/living/carbon/human/dummy/mannequin = new() + client.prefs.dress_preview_mob(mannequin) + observer.appearance = mannequin observer.alpha = 127 + observer.layer = initial(observer.layer) + observer.invisibility = initial(observer.invisibility) + qdel(mannequin) if(client.prefs.be_random_name) client.prefs.real_name = random_name(client.prefs.identifying_gender) @@ -427,7 +431,7 @@ if(ticker.random_players) new_character.gender = pick(MALE, FEMALE) client.prefs.real_name = random_name(new_character.gender) - client.prefs.randomize_appearance_for(new_character) + client.prefs.randomize_appearance_and_body_for(new_character) else client.prefs.copy_to(new_character) diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm index e93f2af24c..527ad8b70b 100644 --- a/code/modules/mob/new_player/preferences_setup.dm +++ b/code/modules/mob/new_player/preferences_setup.dm @@ -1,6 +1,6 @@ /datum/preferences //The mob should have a gender you want before running this proc. Will run fine without H -/datum/preferences/proc/randomize_appearance_for(var/mob/living/carbon/human/H) +/datum/preferences/proc/randomize_appearance_and_body_for(var/mob/living/carbon/human/H) var/datum/species/current_species = all_species[species ? species : "Human"] set_biological_gender(pick(current_species.genders)) @@ -9,13 +9,12 @@ if(current_species) if(current_species.appearance_flags & HAS_SKIN_TONE) s_tone = random_skin_tone() + if(current_species.appearance_flags & HAS_SKIN_COLOR) + r_skin = rand (0,255) + g_skin = rand (0,255) + b_skin = rand (0,255) if(current_species.appearance_flags & HAS_EYE_COLOR) randomize_eyes_color() - if(current_species.appearance_flags & HAS_UNDERWEAR) - underwear_top = underwear_top_t[pick(underwear_top_t)] - underwear_bottom = underwear_bottom_t[pick(underwear_bottom_t)] - undershirt = undershirt_t[pick(undershirt_t)] - socks = socks_t[pick(socks_t)] if(current_species.appearance_flags & HAS_HAIR_COLOR) randomize_hair_color("hair") randomize_hair_color("facial") @@ -23,13 +22,17 @@ r_skin = rand (0,255) g_skin = rand (0,255) b_skin = rand (0,255) + if(current_species.appearance_flags & HAS_UNDERWEAR) + all_underwear.Cut() + for(var/datum/category_group/underwear/WRC in global_underwear.categories) + var/datum/category_item/underwear/WRI = pick(WRC.items) + all_underwear[WRC.name] = WRI.name - //backbag = 2 backbag = rand(1,4) pdachoice = rand(1,3) age = rand(current_species.min_age, current_species.max_age) - b_type = pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+") + b_type = RANDOM_BLOOD_TYPE if(H) copy_to(H,1) @@ -190,554 +193,74 @@ g_skin = green b_skin = blue +/datum/preferences/proc/dress_preview_mob(var/mob/living/carbon/human/mannequin) + copy_to(mannequin) + if(!dress_mob) + return -/datum/preferences/proc/update_preview_icon() //seriously. This is horrendous. - qdel(preview_icon_front) - qdel(preview_icon_side) - qdel(preview_icon) - - var/g = "m" - if(biological_gender == FEMALE) g = "f" - - var/icon/icobase - var/datum/species/current_species = all_species[species] - - if(current_species) - icobase = current_species.icobase + // Determine what job is marked as 'High' priority, and dress them up as such. + var/datum/job/previewJob + if(job_civilian_low & ASSISTANT) + previewJob = job_master.GetJob("Assistant") else - icobase = 'icons/mob/human_races/r_human.dmi' + for(var/datum/job/job in job_master.occupations) + var/job_flag + switch(job.department_flag) + if(CIVILIAN) + job_flag = job_civilian_high + if(MEDSCI) + job_flag = job_medsci_high + if(ENGSEC) + job_flag = job_engsec_high + if(job.flag == job_flag) + previewJob = job + break - preview_icon = new /icon(icobase, "") - for(var/name in BP_ALL) - if(organ_data[name] == "amputated") - continue - if(organ_data[name] == "cyborg") - var/datum/robolimb/R - if(rlimb_data[name]) R = all_robolimbs[rlimb_data[name]] - if(!R) R = basic_robolimb - if(name in list(BP_TORSO, BP_GROIN, BP_HEAD)) - preview_icon.Blend(icon(R.icon, "[name]_[g]"), ICON_OVERLAY) - else - preview_icon.Blend(icon(R.icon, "[name]"), ICON_OVERLAY) - continue - var/icon/limb_icon - if(name in list(BP_TORSO, BP_GROIN, BP_HEAD)) - limb_icon = new /icon(icobase, "[name]_[g]") - else - limb_icon = new /icon(icobase, "[name]") - // Skin color - if(current_species && (current_species.appearance_flags & HAS_SKIN_COLOR)) - limb_icon.Blend(rgb(r_skin, g_skin, b_skin), ICON_ADD) - // Skin tone - if(current_species && (current_species.appearance_flags & HAS_SKIN_TONE)) - if (s_tone >= 0) - limb_icon.Blend(rgb(s_tone, s_tone, s_tone), ICON_ADD) - else - limb_icon.Blend(rgb(-s_tone, -s_tone, -s_tone), ICON_SUBTRACT) - preview_icon.Blend(limb_icon, ICON_OVERLAY) - - //Tail - if(current_species && (current_species.tail)) - var/icon/temp = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[current_species.tail]_s") - if(current_species && (current_species.appearance_flags & HAS_SKIN_COLOR)) - temp.Blend(rgb(r_skin, g_skin, b_skin), ICON_ADD) - if(current_species && (current_species.appearance_flags & HAS_SKIN_TONE)) - if (s_tone >= 0) - temp.Blend(rgb(s_tone, s_tone, s_tone), ICON_ADD) - else - temp.Blend(rgb(-s_tone, -s_tone, -s_tone), ICON_SUBTRACT) - preview_icon.Blend(temp, ICON_OVERLAY) - - // This is absolute garbage but whatever. It will do until this entire file can be rewritten without crashes. - var/use_eye_icon = "eyes_s" - var/list/use_eye_data = current_species.has_limbs[BP_HEAD] - if(islist(use_eye_data)) - var/use_eye_path = use_eye_data["path"] - var/obj/item/organ/external/head/temp_head = new use_eye_path () - if(istype(temp_head)) - use_eye_icon = temp_head.eye_icon - qdel(temp_head) - - var/icon/eyes_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = use_eye_icon) - if ((current_species && (current_species.appearance_flags & HAS_EYE_COLOR))) - eyes_s.Blend(rgb(r_eyes, g_eyes, b_eyes), ICON_ADD) - - var/datum/sprite_accessory/hair_style = hair_styles_list[h_style] - if(hair_style) - var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") - hair_s.Blend(rgb(r_hair, g_hair, b_hair), ICON_ADD) - eyes_s.Blend(hair_s, ICON_OVERLAY) - - var/datum/sprite_accessory/facial_hair_style = facial_hair_styles_list[f_style] - if(facial_hair_style) - var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") - facial_s.Blend(rgb(r_facial, g_facial, b_facial), ICON_ADD) - eyes_s.Blend(facial_s, ICON_OVERLAY) - - var/icon/underwear_top_s = null - if(underwear_top && current_species.appearance_flags & HAS_UNDERWEAR) - underwear_top_s = new/icon("icon" = 'icons/mob/human.dmi', "icon_state" = underwear_top) - var/icon/underwear_bottom_s = null - if(underwear_bottom && current_species.appearance_flags & HAS_UNDERWEAR) - underwear_bottom_s = new/icon("icon" = 'icons/mob/human.dmi', "icon_state" = underwear_bottom) - - var/icon/undershirt_s = null - if(undershirt && current_species.appearance_flags & HAS_UNDERWEAR) - undershirt_s = new/icon("icon" = 'icons/mob/human.dmi', "icon_state" = undershirt) - - var/icon/socks_s = null - if(socks && current_species.appearance_flags & HAS_UNDERWEAR) - socks_s = new/icon("icon" = 'icons/mob/human.dmi', "icon_state" = socks) - - var/icon/clothes_s = null - if(job_civilian_low & ASSISTANT)//This gives the preview icon clothes depending on which job(if any) is set to 'high' - clothes_s = new /icon('icons/mob/uniform.dmi', "grey_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(backbag == 2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - else if(backbag == 3 || backbag == 4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - - else if(job_civilian_high)//I hate how this looks, but there's no reason to go through this switch if it's empty - switch(job_civilian_high) - if(HOP) - clothes_s = new /icon('icons/mob/uniform.dmi', "hop_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "ianshirt"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(BARTENDER) - clothes_s = new /icon('icons/mob/uniform.dmi', "ba_suit_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "tophat"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(BOTANIST) - clothes_s = new /icon('icons/mob/uniform.dmi', "hydroponics_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "ggloves"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "apron"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "nymph"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-hyd"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CHEF) - clothes_s = new /icon('icons/mob/uniform.dmi', "chef_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "chefhat"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "apronchef"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JANITOR) - clothes_s = new /icon('icons/mob/uniform.dmi', "janitor_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bio_janitor"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(LIBRARIAN) - clothes_s = new /icon('icons/mob/uniform.dmi', "red_suit_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "hairflower"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(QUARTERMASTER) - clothes_s = new /icon('icons/mob/uniform.dmi', "qm_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "poncho"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CARGOTECH) - clothes_s = new /icon('icons/mob/uniform.dmi', "cargotech_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "flat_cap"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(MINER) - clothes_s = new /icon('icons/mob/uniform.dmi', "miner_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "bearpelt"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(LAWYER) - clothes_s = new /icon('icons/mob/uniform.dmi', "internalaffairs_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon(INV_R_HAND_DEF_ICON, "briefcase"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "suitjacket_blue"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CHAPLAIN) - clothes_s = new /icon('icons/mob/uniform.dmi', "chapblack_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "imperium_monk"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CLOWN) - clothes_s = new /icon('icons/mob/uniform.dmi', "clown_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "clown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/mask.dmi', "clown"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "clownpack"), ICON_OVERLAY) - if(MIME) - clothes_s = new /icon('icons/mob/uniform.dmi', "mime_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "lgloves"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/mask.dmi', "mime"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "beret"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "suspenders"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - - else if(job_medsci_high) - switch(job_medsci_high) - if(RD) - clothes_s = new /icon('icons/mob/uniform.dmi', "director_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "petehat"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-tox"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(SCIENTIST) - clothes_s = new /icon('icons/mob/uniform.dmi', "sciencewhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_tox_open"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "metroid"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-tox"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(XENOBIOLOGIST) - clothes_s = new /icon('icons/mob/uniform.dmi', "sciencewhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_tox_open"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "metroid"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-tox"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CHEMIST) - clothes_s = new /icon('icons/mob/uniform.dmi', "chemistrywhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labgreen"), ICON_OVERLAY) + if(previewJob) + mannequin.job = previewJob.title + previewJob.equip_preview(mannequin, player_alt_titles[previewJob.title]) + var/list/equipped_slots = list() //If more than one item takes the same slot only spawn the first + for(var/thing in gear) + var/datum/gear/G = gear_datums[thing] + if(G) + var/permitted = 0 + if(G.allowed_roles) + for(var/job_name in G.allowed_roles) + if(previewJob.title == job_name) + permitted = 1 else - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_chem_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-chem"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CMO) - clothes_s = new /icon('icons/mob/uniform.dmi', "cmo_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bio_cmo"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_cmo_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(DOCTOR) - clothes_s = new /icon('icons/mob/uniform.dmi', "medical_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "surgeon"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(GENETICIST) - clothes_s = new /icon('icons/mob/uniform.dmi', "geneticswhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "monkeysuit"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_gen_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-gen"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(VIROLOGIST) - clothes_s = new /icon('icons/mob/uniform.dmi', "virologywhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/mask.dmi', "sterile"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_vir_open"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "plaguedoctor"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-vir"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(ROBOTICIST) - clothes_s = new /icon('icons/mob/uniform.dmi', "robotics_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon(INV_R_HAND_DEF_ICON, "toolbox_blue"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + permitted = 1 - else if(job_engsec_high) - switch(job_engsec_high) - if(CAPTAIN) - clothes_s = new /icon('icons/mob/uniform.dmi', "captain_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "centcomcaptain"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/head.dmi', "captain"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-cap"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(HOS) - clothes_s = new /icon('icons/mob/uniform.dmi', "hosred_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "hosberet"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(WARDEN) - clothes_s = new /icon('icons/mob/uniform.dmi', "warden_s") - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "slippers_worn"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(DETECTIVE) - clothes_s = new /icon('icons/mob/uniform.dmi', "detective_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/mask.dmi', "cigaron"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "detective"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "detective"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(OFFICER) - clothes_s = new /icon('icons/mob/uniform.dmi', "secred_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "officerberet"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CHIEF) - clothes_s = new /icon('icons/mob/uniform.dmi', "chief_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "hardhat0_white"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon(INV_R_HAND_DEF_ICON, "blueprints"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "engiepack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(ENGINEER) - clothes_s = new /icon('icons/mob/uniform.dmi', "engine_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "hardhat0_yellow"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "hazard"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "engiepack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(ATMOSTECH) - clothes_s = new /icon('icons/mob/uniform.dmi', "atmos_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "firesuit"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(G.whitelisted && (G.whitelisted != mannequin.species.name)) + permitted = 0 - if(AI)//Gives AI and borgs assistant-wear, so they can still customize their character - clothes_s = new /icon('icons/mob/uniform.dmi', "grey_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "straight_jacket"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "cardborg_h"), ICON_OVERLAY) - if(backbag == 2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - else if(backbag == 3 || backbag == 4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CYBORG) - clothes_s = new /icon('icons/mob/uniform.dmi', "grey_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "cardborg"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "cardborg_h"), ICON_OVERLAY) - if(backbag == 2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - else if(backbag == 3 || backbag == 4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(!permitted) + continue - if(disabilities & NEARSIGHTED) - preview_icon.Blend(new /icon('icons/mob/eyes.dmi', "glasses"), ICON_OVERLAY) + if(G.slot && !(G.slot in equipped_slots)) + equipped_slots += G.slot + var/metadata = gear[G.display_name] + mannequin.equip_to_slot_or_del(G.spawn_item(mannequin, metadata), G.slot) + mannequin.update_icons() - preview_icon.Blend(eyes_s, ICON_OVERLAY) - if(underwear_top_s) - preview_icon.Blend(underwear_top_s, ICON_OVERLAY) - if(underwear_bottom_s) - preview_icon.Blend(underwear_bottom_s, ICON_OVERLAY) - if(undershirt_s) - preview_icon.Blend(undershirt_s, ICON_OVERLAY) - if(socks_s) - preview_icon.Blend(socks_s, ICON_OVERLAY) - if(clothes_s) - preview_icon.Blend(clothes_s, ICON_OVERLAY) - preview_icon_front = new(preview_icon, dir = SOUTH) - preview_icon_side = new(preview_icon, dir = WEST) +/datum/preferences/proc/update_preview_icon() + var/mob/living/carbon/human/dummy/mannequin/mannequin = get_mannequin(client_ckey) + mannequin.delete_inventory(TRUE) + dress_preview_mob(mannequin) - qdel(eyes_s) - qdel(underwear_top_s) - qdel(underwear_bottom_s) - qdel(undershirt_s) - qdel(socks_s) - qdel(clothes_s) + preview_icon = icon('icons/effects/effects.dmi', "nothing") + preview_icon.Scale(48+32, 16+32) + + mannequin.dir = NORTH + var/icon/stamp = getFlatIcon(mannequin) + preview_icon.Blend(stamp, ICON_OVERLAY, 25, 17) + + mannequin.dir = WEST + stamp = getFlatIcon(mannequin) + preview_icon.Blend(stamp, ICON_OVERLAY, 1, 9) + + mannequin.dir = SOUTH + stamp = getFlatIcon(mannequin) + preview_icon.Blend(stamp, ICON_OVERLAY, 49, 1) + + preview_icon.Scale(preview_icon.Width() * 2, preview_icon.Height() * 2) // Scaling here to prevent blurring in the browser. \ No newline at end of file diff --git a/code/modules/mob/new_player/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories.dm index 3f8d01d3c0..b9bfba7adb 100644 --- a/code/modules/mob/new_player/sprite_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories.dm @@ -170,6 +170,10 @@ name = "Ponytail 6" icon_state = "hair_ponytail6" + fringetail + name = "Fringetail" + icon_state = "hair_fringetail" + sideponytail name = "Side Ponytail" icon_state = "hair_stail" @@ -198,6 +202,10 @@ name = "Pompadour" icon_state = "hair_pompadour" + sleeze + name = "Sleeze" + icon_state = "hair_sleeze" + quiff name = "Quiff" icon_state = "hair_quiff" @@ -293,6 +301,14 @@ name = "Big Afro" icon_state = "hair_bigafro" + rows + name = "Rows" + icon_state = "hair_rows1" + + rows2 + name = "Rows 2" + icon_state = "hair_rows2" + sargeant name = "Flat Top" icon_state = "hair_sargeant" diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index ec47effaba..f7aaa52f78 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -1,29 +1,15 @@ /mob/proc/say() return -/mob/verb/whisper() +/mob/verb/whisper(message as text) set name = "Whisper" set category = "IC" - return + + usr.say(message,whispering=1) /mob/verb/say_verb(message as text) set name = "Say" set category = "IC" - if(say_disabled) //This is here to try to identify lag problems - usr << "\red Speech is currently admin-disabled." - return - //Let's try to make users fix their errors - we try to detect single, out-of-place letters and 'unintended' words - /* - var/first_letter = copytext(message,1,2) - if((copytext(message,2,3) == " " && first_letter != "I" && first_letter != "A" && first_letter != ";") || cmptext(copytext(message,1,5), "say ") || cmptext(copytext(message,1,4), "me ") || cmptext(copytext(message,1,6), "looc ") || cmptext(copytext(message,1,5), "ooc ") || cmptext(copytext(message,2,6), "say ")) - var/response = alert(usr, "Do you really want to say this using the *say* verb?\n\n[message]\n", "Confirm your message", "Yes", "Edit message", "No") - if(response == "Edit message") - message = input(usr, "Please edit your message carefully:", "Edit message", message) - if(!message) - return - else if(response == "No") - return - */ set_typing_indicator(0) usr.say(message) @@ -62,7 +48,7 @@ /mob/proc/say_understands(var/mob/other,var/datum/language/speaking = null) - if (src.stat == 2) //Dead + if (src.stat == DEAD) return 1 //Universal speak makes everything understandable, for obvious reasons. diff --git a/code/modules/organs/blood.dm b/code/modules/organs/blood.dm index 1bae132aed..2c5f24984a 100644 --- a/code/modules/organs/blood.dm +++ b/code/modules/organs/blood.dm @@ -126,7 +126,7 @@ var/const/BLOOD_VOLUME_SURVIVE = 40 //Bleeding out var/blood_max = 0 for(var/obj/item/organ/external/temp in organs) - if(!(temp.status & ORGAN_BLEEDING) || (temp.status & ORGAN_ROBOT)) + if(!(temp.status & ORGAN_BLEEDING) || (temp.robotic >= ORGAN_ROBOT)) continue for(var/datum/wound/W in temp.wounds) if(W.bleeding()) blood_max += W.damage / 40 @@ -266,9 +266,11 @@ proc/blood_splatter(var/target,var/datum/reagent/blood/source,var/large) var/obj/effect/decal/cleanable/blood/B var/decal_type = /obj/effect/decal/cleanable/blood/splatter var/turf/T = get_turf(target) + var/synth = 0 if(istype(source,/mob/living/carbon/human)) var/mob/living/carbon/human/M = source + if(M.isSynthetic()) synth = 1 source = M.get_blood(M.vessel) // Are we dripping or splattering? @@ -297,6 +299,7 @@ proc/blood_splatter(var/target,var/datum/reagent/blood/source,var/large) // Update appearance. if(source.data["blood_colour"]) B.basecolor = source.data["blood_colour"] + B.synthblood = synth B.update_icon() // Update blood information. diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 44376e9110..13eef2eaf4 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -10,9 +10,10 @@ var/list/organ_cache = list() var/parent_organ = BP_TORSO // Organ holding this object. // Status tracking. - var/status = 0 // Various status flags (such as robotic) + var/status = 0 // Various status flags var/vital // Lose a vital limb, die immediately. var/damage = 0 // Current damage to the organ + var/robotic = 0 // Reference data. var/mob/living/carbon/human/owner // Current mob owning the organ. @@ -69,6 +70,8 @@ var/list/organ_cache = list() blood_DNA[dna.unique_enzymes] = dna.b_type if(internal) holder.internal_organs |= src + else + species = all_species["Human"] /obj/item/organ/proc/set_dna(var/datum/dna/new_dna) if(new_dna) @@ -77,7 +80,7 @@ var/list/organ_cache = list() blood_DNA[dna.unique_enzymes] = dna.b_type /obj/item/organ/proc/die() - if(status & ORGAN_ROBOT) + if(robotic >= ORGAN_ROBOT) return damage = max_damage status |= ORGAN_DEAD @@ -96,10 +99,10 @@ var/list/organ_cache = list() // Don't process if we're in a freezer, an MMI or a stasis bag.or a freezer or something I dunno if(istype(loc,/obj/item/device/mmi)) return - if(istype(loc,/obj/structure/closet/body_bag/cryobag) || istype(loc,/obj/structure/closet/crate/freezer) || istype(loc,/obj/item/weapon/storage/box/freezer)) + if(istype(loc,/obj/structure/closet/body_bag/cryobag) || istype(loc,/obj/structure/closet/crate/freezer) || istype(loc,/obj/item/weapon/storage/box/freezer) || istype(loc,/obj/item/weapon/gripper/no_use/organ)) return //Process infections - if ((status & ORGAN_ROBOT) || (owner && owner.species && (owner.species.flags & IS_PLANT))) + if ((robotic >= ORGAN_ROBOT) || (owner && owner.species && (owner.species.flags & IS_PLANT))) germ_level = 0 return @@ -231,7 +234,7 @@ var/list/organ_cache = list() //Note: external organs have their own version of this proc /obj/item/organ/proc/take_damage(amount, var/silent=0) - if(src.status & ORGAN_ROBOT) + if(src.robotic >= ORGAN_ROBOT) src.damage = between(0, src.damage + (amount * 0.8), max_damage) else src.damage = between(0, src.damage + amount, max_damage) @@ -243,19 +246,20 @@ var/list/organ_cache = list() owner.custom_pain("Something inside your [parent.name] hurts a lot.", 1) /obj/item/organ/proc/robotize() //Being used to make robutt hearts, etc - status = 0 - status |= ORGAN_ASSISTED - status |= ORGAN_ROBOT - + robotic = ORGAN_ROBOT + src.status &= ~ORGAN_BROKEN + src.status &= ~ORGAN_BLEEDING + src.status &= ~ORGAN_SPLINTED + src.status &= ~ORGAN_CUT_AWAY /obj/item/organ/proc/mechassist() //Used to add things like pacemakers, etc - status = 0 - status |= ORGAN_ASSISTED + robotize() + robotic = ORGAN_ASSISTED min_bruised_damage = 15 min_broken_damage = 35 /obj/item/organ/emp_act(severity) - if(!(status & ORGAN_ROBOT)) + if(!(robotic >= ORGAN_ROBOT)) return switch (severity) if (1) @@ -316,7 +320,7 @@ var/list/organ_cache = list() /obj/item/organ/proc/bitten(mob/user) - if(status & ORGAN_ROBOT) + if(robotic >= ORGAN_ROBOT) return user << "You take an experimental bite out of \the [src]." @@ -342,9 +346,9 @@ var/list/organ_cache = list() /obj/item/organ/attack_self(mob/user as mob) // Convert it to an edible form, yum yum. - if(!(status & ORGAN_ROBOT) && user.a_intent == I_HELP && user.zone_sel.selecting == O_MOUTH) + if(!(robotic >= ORGAN_ROBOT) && user.a_intent == I_HELP && user.zone_sel.selecting == O_MOUTH) bitten(user) return /obj/item/organ/proc/can_feel_pain() - return !(status & (ORGAN_ROBOT|ORGAN_DESTROYED)) && !(species.flags & NO_PAIN) + return !(robotic >= (ORGAN_ROBOT|ORGAN_DESTROYED)) && !(species.flags & NO_PAIN) diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index 0a08817ef0..c084c06170 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -44,7 +44,7 @@ var/list/wounds = list() // wound datum list. var/number_wounds = 0 // number of wounds, which is NOT wounds.len! var/obj/item/organ/external/parent // Master-limb. - var/list/children // Sub-limbs. + var/list/children = list() // Sub-limbs. var/list/internal_organs = list() // Internal organs of this body part var/sabotaged = 0 // If a prosthetic limb is emagged, it will detonate when it fails. var/list/implants = list() // Currently implanted objects. @@ -92,7 +92,7 @@ return ..() /obj/item/organ/external/emp_act(severity) - if(!(status & ORGAN_ROBOT)) + if(!(robotic >= ORGAN_ROBOT)) return switch (severity) if (1) @@ -225,7 +225,8 @@ ****************************************************/ /obj/item/organ/external/proc/is_damageable(var/additional_damage = 0) - return (vital || brute_dam + burn_dam + additional_damage < max_damage) + //Continued damage to vital organs can kill you, and robot organs don't count towards total damage so no need to cap them. + return (vital || (robotic >= ORGAN_ROBOT) || brute_dam + burn_dam + additional_damage < max_damage) /obj/item/organ/external/take_damage(brute, burn, sharp, edge, used_weapon = null, list/forbidden_limbs = list()) if((brute <= 0) && (burn <= 0)) @@ -243,12 +244,12 @@ brute -= brute / 2 if(status & ORGAN_BROKEN && prob(40) && brute) - if(!((species.flags & NO_PAIN) || (status & ORGAN_ROBOT))) + if(!((species.flags & NO_PAIN) || (robotic >= ORGAN_ROBOT))) owner.emote("scream") //getting hit on broken hand hurts if(used_weapon) add_autopsy_data("[used_weapon]", brute + burn) - var/can_cut = (prob(brute*2) || sharp) && !(status & ORGAN_ROBOT) + var/can_cut = (prob(brute*2) || sharp) && (robotic < ORGAN_ROBOT) // If the limbs can break, make sure we don't exceed the maximum damage a limb can take before breaking // Non-vital organs are limited to max_damage. You can't kill someone by bludeonging their arm all the way to 200 -- you can @@ -330,7 +331,7 @@ return update_icon() /obj/item/organ/external/proc/heal_damage(brute, burn, internal = 0, robo_repair = 0) - if(status & ORGAN_ROBOT && !robo_repair) + if(robotic >= ORGAN_ROBOT && !robo_repair) return //Heal damage on the individual wounds @@ -355,12 +356,61 @@ var/result = update_icon() return result +//Helper proc used by various tools for repairing robot limbs +/obj/item/organ/external/proc/robo_repair(var/repair_amount, var/damage_type, var/damage_desc, obj/item/tool, mob/living/user) + if((src.robotic < ORGAN_ROBOT)) + return 0 + + var/damage_amount + switch(damage_type) + if(BRUTE) damage_amount = brute_dam + if(BURN) damage_amount = burn_dam + if("omni") damage_amount = max(brute_dam,burn_dam) + else return 0 + + if(!damage_amount) + user << "Nothing to fix!" + return 0 + + if(damage_amount >= ROBOLIMB_REPAIR_CAP) + user << "The damage is far too severe to patch over externally." + return 0 + + if(user == src.owner) + var/grasp + if(user.l_hand == tool && (src.body_part & (ARM_LEFT|HAND_LEFT))) + grasp = "l_hand" + else if(user.r_hand == tool && (src.body_part & (ARM_RIGHT|HAND_RIGHT))) + grasp = "r_hand" + + if(grasp) + user << "You can't reach your [src.name] while holding [tool] in your [owner.get_bodypart_name(grasp)]." + return 0 + + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + if(!do_mob(user, owner, 10)) + user << "You must stand still to do that." + return 0 + + switch(damage_type) + if(BRUTE) src.heal_damage(repair_amount, 0, 0, 1) + if(BURN) src.heal_damage(0, repair_amount, 0, 1) + if("omni")src.heal_damage(repair_amount, repair_amount, 0, 1) + + if(damage_desc) + if(user == src.owner) + user.visible_message("\The [user] patches [damage_desc] on \his [src.name] with [tool].") + else + user.visible_message("\The [user] patches [damage_desc] on [owner]'s [src.name] with [tool].") + + return 1 + + /* This function completely restores a damaged organ to perfect condition. */ /obj/item/organ/external/rejuvenate(var/ignore_prosthetic_prefs) damage_state = "00" - status = 0 brute_dam = 0 burn_dam = 0 @@ -411,7 +461,7 @@ This function completely restores a damaged organ to perfect condition. //moved this before the open_wound check so that having many small wounds for example doesn't somehow protect you from taking internal damage (because of the return) //Possibly trigger an internal wound, too. var/local_damage = brute_dam + burn_dam + damage - if(damage > 15 && type != BURN && local_damage > 30 && prob(damage) && !(status & ORGAN_ROBOT)) + if(damage > 15 && type != BURN && local_damage > 30 && prob(damage) && (robotic < ORGAN_ROBOT)) var/datum/wound/internal_bleeding/I = new (min(damage - 15, 15)) wounds += I owner.custom_pain("You feel something rip in your [name]!", 1) @@ -429,7 +479,7 @@ This function completely restores a damaged organ to perfect condition. var/datum/wound/W = pick(compatible_wounds) W.open_wound(damage) if(prob(25)) - if(status & ORGAN_ROBOT) + if(robotic >= ORGAN_ROBOT) owner.visible_message("The damage to [owner.name]'s [name] worsens.",\ "The damage to your [name] worsens.",\ "You hear the screech of abused metal.") @@ -467,7 +517,7 @@ This function completely restores a damaged organ to perfect condition. /obj/item/organ/external/proc/need_process() if(status & (ORGAN_CUT_AWAY|ORGAN_BLEEDING|ORGAN_BROKEN|ORGAN_DESTROYED|ORGAN_SPLINTED|ORGAN_DEAD|ORGAN_MUTATED)) return 1 - if(brute_dam || burn_dam) + if((brute_dam || burn_dam) && (robotic < ORGAN_ROBOT)) //Robot limbs don't autoheal and thus don't need to process when damaged return 1 if(last_dam != brute_dam + burn_dam) // Process when we are fully healed up. last_dam = brute_dam + burn_dam @@ -523,7 +573,7 @@ Note that amputating the affected organ does in fact remove the infection from t */ /obj/item/organ/external/proc/update_germs() - if(status & (ORGAN_ROBOT) || (owner.species && owner.species.flags & IS_PLANT)) //Robotic limbs shouldn't be infected, nor should nonexistant limbs. + if(robotic >= ORGAN_ROBOT || (owner.species && owner.species.flags & IS_PLANT)) //Robotic limbs shouldn't be infected, nor should nonexistant limbs. germ_level = 0 return @@ -581,12 +631,12 @@ Note that amputating the affected organ does in fact remove the infection from t //spread the infection to child and parent organs if (children) for (var/obj/item/organ/external/child in children) - if (child.germ_level < germ_level && !(child.status & ORGAN_ROBOT)) + if (child.germ_level < germ_level && (child.robotic < ORGAN_ROBOT)) if (child.germ_level < INFECTION_LEVEL_ONE*2 || prob(30)) child.germ_level++ if (parent) - if (parent.germ_level < germ_level && !(parent.status & ORGAN_ROBOT)) + if (parent.germ_level < germ_level && (parent.robotic < ORGAN_ROBOT)) if (parent.germ_level < INFECTION_LEVEL_ONE*2 || prob(30)) parent.germ_level++ @@ -602,10 +652,10 @@ Note that amputating the affected organ does in fact remove the infection from t //Updating wounds. Handles wound natural I had some free spachealing, internal bleedings and infections /obj/item/organ/external/proc/update_wounds() - if((status & ORGAN_ROBOT)) //Robotic limbs don't heal or get worse. - for(var/datum/wound/W in wounds) - if(W.damage <= 0) - wounds -= W + if((robotic >= ORGAN_ROBOT)) //Robotic limbs don't heal or get worse. + for(var/datum/wound/W in wounds) //Repaired wounds disappear though + if(W.damage <= 0) //and they disappear right away + wounds -= W //TODO: robot wounds for robot limbs return for(var/datum/wound/W in wounds) @@ -675,7 +725,7 @@ Note that amputating the affected organ does in fact remove the infection from t else brute_dam += W.damage - if(!(status & ORGAN_ROBOT) && W.bleeding() && (H && H.should_have_organ(O_HEART))) + if(!(robotic >= ORGAN_ROBOT) && W.bleeding() && (H && !H.should_have_organ(O_HEART))) W.bleed_timer-- status |= ORGAN_BLEEDING @@ -688,7 +738,7 @@ Note that amputating the affected organ does in fact remove the infection from t status |= ORGAN_BLEEDING //Bone fractures - if(config.bones_can_break && brute_dam > min_broken_damage * config.organ_health_multiplier && !(status & ORGAN_ROBOT)) + if(config.bones_can_break && brute_dam > min_broken_damage * config.organ_health_multiplier && !(robotic >= ORGAN_ROBOT)) src.fracture() // new damage icon system @@ -742,7 +792,7 @@ Note that amputating the affected organ does in fact remove the infection from t switch(disintegrate) if(DROPLIMB_EDGE) if(!clean) - var/gore_sound = "[(status & ORGAN_ROBOT) ? "tortured metal" : "ripping tendons and flesh"]" + var/gore_sound = "[(robotic >= ORGAN_ROBOT) ? "tortured metal" : "ripping tendons and flesh"]" owner.visible_message( "\The [owner]'s [src.name] flies off in an arc!",\ "Your [src.name] goes flying off!",\ @@ -750,7 +800,7 @@ Note that amputating the affected organ does in fact remove the infection from t if(DROPLIMB_BURN) if(cannot_gib) return - var/gore = "[(status & ORGAN_ROBOT) ? "": " of burning flesh"]" + var/gore = "[(robotic >= ORGAN_ROBOT) ? "": " of burning flesh"]" owner.visible_message( "\The [owner]'s [src.name] flashes away into ashes!",\ "Your [src.name] flashes away into ashes!",\ @@ -758,8 +808,8 @@ Note that amputating the affected organ does in fact remove the infection from t if(DROPLIMB_BLUNT) if(cannot_gib) return - var/gore = "[(status & ORGAN_ROBOT) ? "": " in shower of gore"]" - var/gore_sound = "[(status & ORGAN_ROBOT) ? "rending sound of tortured metal" : "sickening splatter of gore"]" + var/gore = "[(robotic >= ORGAN_ROBOT) ? "": " in shower of gore"]" + var/gore_sound = "[(status >= ORGAN_ROBOT) ? "rending sound of tortured metal" : "sickening splatter of gore"]" owner.visible_message( "\The [owner]'s [src.name] explodes[gore]!",\ "Your [src.name] explodes[gore]!",\ @@ -781,7 +831,7 @@ Note that amputating the affected organ does in fact remove the infection from t parent_organ.update_damages() else var/obj/item/organ/external/stump/stump = new (victim, 0, src) - if(status & ORGAN_ROBOT) + if(robotic >= ORGAN_ROBOT) stump.robotize() stump.wounds |= W victim.organs |= stump @@ -813,7 +863,7 @@ Note that amputating the affected organ does in fact remove the infection from t qdel(src) if(DROPLIMB_BLUNT) var/obj/effect/decal/cleanable/blood/gibs/gore - if(status & ORGAN_ROBOT) + if(robotic >= ORGAN_ROBOT) gore = new /obj/effect/decal/cleanable/blood/gibs/robot(get_turf(victim)) else gore = new /obj/effect/decal/cleanable/blood/gibs(get_turf(victim)) @@ -920,7 +970,7 @@ Note that amputating the affected organ does in fact remove the infection from t return rval /obj/item/organ/external/proc/fracture() - if(status & ORGAN_ROBOT) + if(robotic >= ORGAN_ROBOT) return //ORGAN_BROKEN doesn't have the same meaning for robot limbs if((status & ORGAN_BROKEN) || cannot_break) return @@ -960,7 +1010,7 @@ Note that amputating the affected organ does in fact remove the infection from t return /obj/item/organ/external/proc/mend_fracture() - if(status & ORGAN_ROBOT) + if(robotic >= ORGAN_ROBOT) return 0 //ORGAN_BROKEN doesn't have the same meaning for robot limbs if(brute_dam > min_broken_damage * config.organ_health_multiplier) return 0 //will just immediately fracture again @@ -970,7 +1020,7 @@ Note that amputating the affected organ does in fact remove the infection from t /obj/item/organ/external/robotize(var/company, var/skip_prosthetics = 0, var/keep_organs = 0) - if(status & ORGAN_ROBOT) + if(robotic >= ORGAN_ROBOT) return ..() @@ -982,7 +1032,11 @@ Note that amputating the affected organ does in fact remove the infection from t R = basic_robolimb if(R) force_icon = R.icon - name = "robotic [initial(name)]" + if(R.lifelike) + robotic = ORGAN_LIFELIKE + name = "[initial(name)]" + else + name = "robotic [initial(name)]" desc = "[R.desc] It looks like it was produced by [R.company]." dislocated = -1 @@ -995,9 +1049,6 @@ Note that amputating the affected organ does in fact remove the infection from t if(owner) - if(!skip_prosthetics) - owner.full_prosthetic = null // Will be rechecked next isSynthetic() call. - if(!keep_organs) for(var/obj/item/organ/thing in internal_organs) if(istype(thing)) @@ -1015,7 +1066,7 @@ Note that amputating the affected organ does in fact remove the infection from t return 1 /obj/item/organ/external/proc/mutate() - if(src.status & ORGAN_ROBOT) + if(src.robotic >= ORGAN_ROBOT) return src.status |= ORGAN_MUTATED if(owner) owner.update_body() @@ -1037,7 +1088,7 @@ Note that amputating the affected organ does in fact remove the infection from t return !is_dislocated() && !(status & (ORGAN_MUTATED|ORGAN_DEAD)) /obj/item/organ/external/proc/is_malfunctioning() - return ((status & ORGAN_ROBOT) && (brute_dam + burn_dam) >= 10 && prob(brute_dam + burn_dam)) + return ((robotic >= ORGAN_ROBOT) && (brute_dam + burn_dam) >= 10 && prob(brute_dam + burn_dam)) /obj/item/organ/external/proc/embed(var/obj/item/weapon/W, var/silent = 0) if(!owner || loc != owner) @@ -1057,7 +1108,7 @@ Note that amputating the affected organ does in fact remove the infection from t if(!owner) return - var/is_robotic = status & ORGAN_ROBOT + var/is_robotic = robotic >= ORGAN_ROBOT var/mob/living/carbon/human/victim = owner ..() @@ -1129,29 +1180,39 @@ Note that amputating the affected organ does in fact remove the infection from t if(status & ORGAN_DESTROYED && !is_stump()) . += "tear at [amputation_point] so severe that it hangs by a scrap of flesh" - if(status & ORGAN_ROBOT) + //Handle robotic and synthetic organ damage + if(robotic >= ORGAN_ROBOT) + var/LL //Life-Like, aka only show that it's robotic in heavy damage + if(robotic >= ORGAN_LIFELIKE) + LL = 1 if(brute_dam) switch(brute_dam) if(0 to 20) - . += "some dents" + . += "some [LL ? "cuts" : "dents"]" if(21 to INFINITY) - . += pick("a lot of dents","severe denting") + . += "[LL ? pick("exposed wiring","torn-back synthflesh") : pick("a lot of dents","severe denting")]" + if(brute_dam && burn_dam) . += " and " + if(burn_dam) switch(burn_dam) if(0 to 20) . += "some burns" if(21 to INFINITY) - . += pick("a lot of burns","severe melting") + . += "[LL ? pick("roasted synth-flesh","melted internal wiring") : pick("many burns","scorched metal")]" + if(open) if(brute_dam || burn_dam) - . += " and an open panel" + . += " and " + if(open == 1) + . += "some exposed screws" else . += "an open panel" return + //Normal organic organ damage var/list/wound_descriptors = list() if(open > 1) wound_descriptors["an open incision"] = 1 @@ -1214,7 +1275,6 @@ Note that amputating the affected organ does in fact remove the infection from t // Give them a new cell. owner.internal_organs_by_name["cell"] = new /obj/item/organ/internal/cell(owner,1) - /obj/item/organ/external/groin name = "lower body" organ_tag = BP_GROIN diff --git a/code/modules/organs/organ_icon.dm b/code/modules/organs/organ_icon.dm index 21e1e337b8..98cce7efd3 100644 --- a/code/modules/organs/organ_icon.dm +++ b/code/modules/organs/organ_icon.dm @@ -16,7 +16,7 @@ var/global/list/limb_icon_cache = list() s_tone = null s_col = null h_col = null - if(status & ORGAN_ROBOT) + if(robotic >= ORGAN_ROBOT) return if(species && human.species && species.name != human.species.name) return @@ -30,7 +30,7 @@ var/global/list/limb_icon_cache = list() s_tone = null s_col = null h_col = null - if(status & ORGAN_ROBOT) + if(robotic >= ORGAN_ROBOT) return if(!isnull(dna.GetUIValue(DNA_UI_SKIN_TONE)) && (species.appearance_flags & HAS_SKIN_TONE)) s_tone = dna.GetUIValue(DNA_UI_SKIN_TONE) @@ -110,7 +110,7 @@ var/global/list/limb_icon_cache = list() if(skeletal) mob_icon = new /icon('icons/mob/human_races/r_skeleton.dmi', "[icon_name][gender ? "_[gender]" : ""]") - else if (status & ORGAN_ROBOT) + else if (robotic >= ORGAN_ROBOT) mob_icon = new /icon('icons/mob/human_races/robotic.dmi', "[icon_name][gender ? "_[gender]" : ""]") else mob_icon = new /icon(species.get_icobase(owner, (status & ORGAN_MUTATED)), "[icon_name][gender ? "_[gender]" : ""]") diff --git a/code/modules/organs/organ_stump.dm b/code/modules/organs/organ_stump.dm index ef1fffd01b..3eb2c15e0a 100644 --- a/code/modules/organs/organ_stump.dm +++ b/code/modules/organs/organ_stump.dm @@ -14,7 +14,7 @@ ..(holder, internal) if(istype(limb)) max_damage = limb.max_damage - if((limb.status & ORGAN_ROBOT) && (!parent || (parent.status & ORGAN_ROBOT))) + if((limb.robotic >= ORGAN_ROBOT) && (!parent || (parent.robotic >= ORGAN_ROBOT))) robotize() //if both limb and the parent are robotic, the stump is robotic too /obj/item/organ/external/stump/is_stump() diff --git a/code/modules/organs/pain.dm b/code/modules/organs/pain.dm index 280ea53f49..ea9499fc43 100644 --- a/code/modules/organs/pain.dm +++ b/code/modules/organs/pain.dm @@ -82,7 +82,7 @@ mob/living/carbon/human/proc/handle_pain() var/maxdam = 0 var/obj/item/organ/external/damaged_organ = null for(var/obj/item/organ/external/E in organs) - if(E.status & (ORGAN_DEAD|ORGAN_ROBOT)) continue + if((E.status & ORGAN_DEAD) || E.robotic >= ORGAN_ROBOT) continue var/dam = E.get_damage() // make the choice of the organ depend on damage, // but also sometimes use one of the less damaged ones @@ -94,7 +94,7 @@ mob/living/carbon/human/proc/handle_pain() // Damage to internal organs hurts a lot. for(var/obj/item/organ/I in internal_organs) - if(I.status & (ORGAN_DEAD|ORGAN_ROBOT)) continue + if((I.status & ORGAN_DEAD) || I.robotic >= ORGAN_ROBOT) continue if(I.damage > 2) if(prob(2)) var/obj/item/organ/external/parent = get_organ(I.parent_organ) src.custom_pain("You feel a sharp pain in your [parent.name]", 1) diff --git a/code/modules/organs/robolimbs.dm b/code/modules/organs/robolimbs.dm index e54598d23e..5241747f12 100644 --- a/code/modules/organs/robolimbs.dm +++ b/code/modules/organs/robolimbs.dm @@ -1,6 +1,27 @@ var/list/all_robolimbs = list() +var/list/robolimb_data = list() var/list/chargen_robolimbs = list() var/datum/robolimb/basic_robolimb +var/const/standard_monitor_styles = "blank=ipc_blank;\ + pink=ipc_pink;\ + green=ipc_green,\ + red=ipc_red;\ + blue=ipc_blue;\ + shower=ipc_shower;\ + orange=ipc_orange;\ + nature=ipc_nature;\ + eight=ipc_eight;\ + goggles=ipc_goggles;\ + heart=ipc_heart;\ + monoeye=ipc_monoeye;\ + breakout=ipc_breakout;\ + yellow=ipc_yellow;\ + static=ipc_static;\ + purple=ipc_purple;\ + scroll=ipc_scroll;\ + console=ipc_console;\ + glider=ipc_gol_glider;\ + rainbow=ipc_rainbow" /proc/populate_robolimb_list() basic_robolimb = new() @@ -8,46 +29,169 @@ var/datum/robolimb/basic_robolimb var/datum/robolimb/R = new limb_type() all_robolimbs[R.company] = R if(!R.unavailable_at_chargen) - chargen_robolimbs[R.company] = R + chargen_robolimbs[R.company] = R //List only main brands and solo parts. /datum/robolimb var/company = "Unbranded" // Shown when selecting the limb. var/desc = "A generic unbranded robotic prosthesis." // Seen when examining a limb. var/icon = 'icons/mob/human_races/robotic.dmi' // Icon base to draw from. var/unavailable_at_chargen // If set, not available at chargen. + var/unavailable_to_build // If set, can't be constructed. + var/lifelike // If set, appears organic. + var/blood_color = "#030303" var/list/species_cannot_use = list("Teshari") + var/list/monitor_styles //If empty, the model of limbs offers a head compatible with monitors. + var/parts = BP_ALL //Defines what parts said brand can replace on a body. /datum/robolimb/bishop company = "Bishop" desc = "This limb has a white polymer casing with blue holo-displays." - icon = 'icons/mob/human_races/cyberlimbs/bishop.dmi' + icon = 'icons/mob/human_races/cyberlimbs/bishop/bishop_main.dmi' + unavailable_to_build = 1 -/datum/robolimb/hesphaistos +/datum/robolimb/bishop_alt1 + company = "Bishop - Glyph" + desc = "This limb has a white polymer casing with blue holo-displays." + icon = 'icons/mob/human_races/cyberlimbs/bishop/bishop_alt1.dmi' + unavailable_to_build = 1 + parts = list(BP_HEAD) + +/datum/robolimb/bishop_monitor + company = "Bishop Monitor" + desc = "Bishop Cybernetics' unique spin on a popular prosthetic head model. The themes conflict in an intriguing way." + icon = 'icons/mob/human_races/cyberlimbs/bishop/bishop_monitor.dmi' + unavailable_to_build = 1 + parts = list(BP_HEAD) + monitor_styles = standard_monitor_styles + +/datum/robolimb/hesphiastos company = "Hesphiastos" desc = "This limb has a militaristic black and green casing with gold stripes." - icon = 'icons/mob/human_races/cyberlimbs/hesphaistos.dmi' + icon = 'icons/mob/human_races/cyberlimbs/hesphiastos/hesphiastos_main.dmi' + unavailable_to_build = 1 -/datum/robolimb/zenghu - company = "Zeng-Hu" - desc = "This limb has a rubbery fleshtone covering with visible seams." - icon = 'icons/mob/human_races/cyberlimbs/zenghu.dmi' +/datum/robolimb/hesphiastos_alt1 + company = "Hesphiastos - Frontier" + desc = "A rugged prosthetic head featuring the standard Hesphiastos theme, a visor and an external display." + icon = 'icons/mob/human_races/cyberlimbs/hesphiastos/hesphiastos_alt1.dmi' + unavailable_to_build = 1 + parts = list(BP_HEAD) + monitor_styles = "blank=hesphiastos_alt_off;\ + pink=hesphiastos_alt_pink;\ + orange=hesphiastos_alt_orange;\ + goggles=hesphiastos_alt_goggles;\ + scroll=hesphiastos_alt_scroll;\ + rgb=hesphiastos_alt_rgb;\ + rainbow=hesphiastos_alt_rainbow" -/datum/robolimb/xion - company = "Xion" - desc = "This limb has a minimalist black and red casing." - icon = 'icons/mob/human_races/cyberlimbs/xion.dmi' +/datum/robolimb/hesphiastos_monitor + company = "Hesphiastos Monitor" + desc = "Hesphiastos' unique spin on a popular prosthetic head model. It looks rugged and sturdy." + icon = 'icons/mob/human_races/cyberlimbs/hesphiastos/hesphiastos_monitor.dmi' + unavailable_to_build = 1 + parts = list(BP_HEAD) + monitor_styles = standard_monitor_styles -/datum/robolimb/ipc +/datum/robolimb/morpheus company = "Morpheus" desc = "This limb is simple and functional; no effort has been made to make it look human." - icon = 'icons/mob/human_races/cyberlimbs/ipc.dmi' + icon = 'icons/mob/human_races/cyberlimbs/morpheus/morpheus_main.dmi' + unavailable_to_build = 1 + monitor_styles = standard_monitor_styles -/datum/robolimb/wardtakahashi - company = "Ward-Takahashi" - desc = "This limb features sleek black and white polymers." - icon = 'icons/mob/human_races/cyberlimbs/wardtakahashi.dmi' +/datum/robolimb/morpheus_alt1 + company = "Morpheus - Zenith" + desc = "This limb is simple and functional; no effort has been made to make it look human." + icon = 'icons/mob/human_races/cyberlimbs/morpheus/morpheus_alt1.dmi' + unavailable_to_build = 1 + parts = list(BP_HEAD) /datum/robolimb/veymed company = "Vey-Med" desc = "This high quality limb is nearly indistinguishable from an organic one." - icon = 'icons/mob/human_races/cyberlimbs/veymed.dmi' \ No newline at end of file + icon = 'icons/mob/human_races/cyberlimbs/veymed/veymed_main.dmi' + unavailable_to_build = 1 + lifelike = 1 + blood_color = "#CCCCCC" + +/datum/robolimb/wardtakahashi + company = "Ward-Takahashi" + desc = "This limb features sleek black and white polymers." + icon = 'icons/mob/human_races/cyberlimbs/wardtakahashi/wardtakahashi_main.dmi' + unavailable_to_build = 1 + +/datum/robolimb/wardtakahashi_alt1 + company = "Ward-Takahashi - Shroud" + desc = "This limb features sleek black and white polymers. This one looks more like a helmet of some sort." + icon = 'icons/mob/human_races/cyberlimbs/wardtakahashi/wardtakahashi_alt1.dmi' + unavailable_to_build = 1 + parts = list(BP_HEAD) + +/datum/robolimb/wardtakahashi_monitor + company = "Ward-Takahashi Monitor" + desc = "Ward-Takahashi's unique spin on a popular prosthetic head model. It looks sleek and modern." + icon = 'icons/mob/human_races/cyberlimbs/wardtakahashi/wardtakahashi_monitor.dmi' + unavailable_to_build = 1 + parts = list(BP_HEAD) + monitor_styles = standard_monitor_styles + +/datum/robolimb/xion + company = "Xion" + desc = "This limb has a minimalist black and red casing." + icon = 'icons/mob/human_races/cyberlimbs/xion/xion_main.dmi' + unavailable_to_build = 1 + +/datum/robolimb/xion_alt1 + company = "Xion Mfg. - Breach" + desc = "This limb has a minimalist black and red casing. Looks a bit menacing." + icon = 'icons/mob/human_races/cyberlimbs/xion/xion_alt1.dmi' + unavailable_to_build = 1 + parts = list(BP_HEAD) + +/datum/robolimb/xion_monitor + company = "Xion Mfg. Monitor" + desc = "Xion Mfg.'s unique spin on a popular prosthetic head model. It looks and minimalist and utilitarian." + icon = 'icons/mob/human_races/cyberlimbs/xion/xion_monitor.dmi' + unavailable_to_build = 1 + parts = list(BP_HEAD) + monitor_styles = standard_monitor_styles + +/datum/robolimb/zenghu + company = "Zeng-Hu" + desc = "This limb has a rubbery fleshtone covering with visible seams." + icon = 'icons/mob/human_races/cyberlimbs/zenghu/zenghu_main.dmi' + unavailable_to_build = 1 + +/obj/item/weapon/disk/limb + name = "Limb Blueprints" + desc = "A disk containing the blueprints for prosthetics." + icon = 'icons/obj/cloning.dmi' + icon_state = "datadisk2" + var/company = "" + + //I'm normally opposed to relative pathing, but pls. + bishop + company = "Bishop" + + hesphiastos + company = "Hesphiastos" + + morpheus + company = "Morpheus" + + veymed + company = "Vey-Med" + + wardtakahashi + company = "Ward-Takahashi" + + xion + company = "Xion" + + zenghu + company = "Zeng-Hu" + +/obj/item/weapon/disk/limb/New(var/newloc) + ..() + if(company) + name = "[company] [initial(name)]" diff --git a/code/modules/organs/subtypes/diona.dm b/code/modules/organs/subtypes/diona.dm index c74f1199e7..344ba2f05a 100644 --- a/code/modules/organs/subtypes/diona.dm +++ b/code/modules/organs/subtypes/diona.dm @@ -126,7 +126,7 @@ //DIONA ORGANS. /obj/item/organ/external/diona/removed() - if(status & ORGAN_ROBOT) + if(robotic >= ORGAN_ROBOT) return ..() var/mob/living/carbon/human/H = owner ..() @@ -142,7 +142,7 @@ organ_tag = "special" // Turns into a nymph instantly, no transplanting possible. /obj/item/organ/internal/diona/removed(var/mob/living/user, var/skip_nymph) - if(status & ORGAN_ROBOT) + if(robotic >= ORGAN_ROBOT) return ..() var/mob/living/carbon/human/H = owner ..() diff --git a/code/modules/organs/subtypes/machine.dm b/code/modules/organs/subtypes/machine.dm index 04544035c0..df74367d0d 100644 --- a/code/modules/organs/subtypes/machine.dm +++ b/code/modules/organs/subtypes/machine.dm @@ -25,6 +25,7 @@ organ_tag = "brain" parent_organ = BP_HEAD vital = 1 + var/brain_type = /obj/item/device/mmi var/obj/item/device/mmi/stored_mmi /obj/item/organ/internal/mmi_holder/Destroy() @@ -35,8 +36,10 @@ /obj/item/organ/internal/mmi_holder/New(var/mob/living/carbon/human/new_owner, var/internal) ..(new_owner, internal) - if(!stored_mmi) - stored_mmi = new(src) + var/mob/living/carbon/human/dummy/mannequin/M = new_owner + if(istype(M)) + return + stored_mmi = new brain_type(src) sleep(-1) update_from_mmi() @@ -80,10 +83,8 @@ /obj/item/organ/internal/mmi_holder/posibrain name = "positronic brain interface" - -/obj/item/organ/internal/mmi_holder/posibrain/New() - stored_mmi = new /obj/item/device/mmi/digital/posibrain(src) - ..() + brain_type = /obj/item/device/mmi/digital/posibrain + /obj/item/organ/internal/mmi_holder/posibrain/update_from_mmi() ..() diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index f467cf4dd5..e4bbbec5f6 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -21,6 +21,27 @@ If d1 = 0 and d2 = dir, it's a O-X cable, getting from the center of the tile to If d1 = dir1 and d2 = dir2, it's a full X-X cable, getting from dir1 to dir2 By design, d1 is the smallest direction and d2 is the highest */ +var/list/possible_cable_coil_colours = list( + "White" = COLOR_WHITE, + "Silver" = COLOR_SILVER, + "Gray" = COLOR_GRAY, + "Black" = COLOR_BLACK, + "Red" = COLOR_RED, + "Maroon" = COLOR_MAROON, + "Yellow" = COLOR_YELLOW, + "Olive" = COLOR_OLIVE, + "Lime" = COLOR_GREEN, + "Green" = COLOR_LIME, + "Cyan" = COLOR_CYAN, + "Teal" = COLOR_TEAL, + "Blue" = COLOR_BLUE, + "Navy" = COLOR_NAVY, + "Pink" = COLOR_PINK, + "Purple" = COLOR_PURPLE, + "Orange" = COLOR_ORANGE, + "Beige" = COLOR_BEIGE, + "Brown" = COLOR_BROWN + ) /obj/structure/cable level = 1 @@ -464,6 +485,7 @@ obj/structure/cable/proc/cableColor(var/colorC) slot_flags = SLOT_BELT item_state = "coil" attack_verb = list("whipped", "lashed", "disciplined", "flogged") + stacktype = /obj/item/stack/cable_coil /obj/item/stack/cable_coil/cyborg name = "cable coil synthesizer" @@ -472,7 +494,6 @@ obj/structure/cable/proc/cableColor(var/colorC) matter = null uses_charge = 1 charge_costs = list(1) - stacktype = /obj/item/stack/cable_coil /obj/item/stack/cable_coil/suicide_act(mob/user) if(locate(/obj/item/weapon/stool) in user.loc) @@ -501,22 +522,16 @@ obj/structure/cable/proc/cableColor(var/colorC) var/mob/living/carbon/human/H = A var/obj/item/organ/external/S = H.organs_by_name[user.zone_sel.selecting] - if(!S || !(S.status & ORGAN_ROBOT)) + if(!S || S.robotic < ORGAN_ROBOT || S.open == 3) return ..() - if(S.burn_dam) - if(S.burn_dam < ROBOLIMB_SELF_REPAIR_CAP) - S.heal_damage(0,15,0,1) - user.visible_message("\The [user] repairs some burn damage on \the [H]'s [S.name] with \the [src].") - else if(S.open < 3) - user << "The damage is far too severe to patch over externally." - else - return ..() - else - user << "Nothing to fix!" - return - return ..() + var/use_amt = min(src.amount, ceil(S.burn_dam/3), 5) + if(can_use(use_amt)) + if(S.robo_repair(3*use_amt, BURN, "some damaged wiring", src, user)) + src.use(use_amt) + else + return ..() /obj/item/stack/cable_coil/update_icon() if (!color) @@ -531,6 +546,17 @@ obj/structure/cable/proc/cableColor(var/colorC) icon_state = "coil" name = "cable coil" +/obj/item/stack/cable_coil/proc/set_cable_color(var/selected_color, var/user) + if(!selected_color) + return + + var/final_color = possible_cable_coil_colours[selected_color] + if(!final_color) + final_color = possible_cable_coil_colours["Red"] + selected_color = "red" + color = final_color + user << "You change \the [src]'s color to [lowertext(selected_color)]." + /obj/item/stack/cable_coil/proc/update_wclass() if(amount == 1) w_class = 1.0 @@ -571,42 +597,17 @@ obj/structure/cable/proc/cableColor(var/colorC) set name = "Change Colour" set category = "Object" - var/list/possible_colours = list ("Yellow", "Green", "Pink", "Blue", "Orange", "Cyan", "Red") - var/selected_type = input("Pick new colour.", "Cable Colour", null, null) as null|anything in possible_colours - - if(selected_type) - switch(selected_type) - if("Yellow") - color = COLOR_YELLOW - if("Green") - color = COLOR_LIME - if("Pink") - color = COLOR_PINK - if("Blue") - color = COLOR_BLUE - if("Orange") - color = COLOR_ORANGE - if("Cyan") - color = COLOR_CYAN - else - color = COLOR_RED - usr << "You change your cable coil's colour to [selected_type]" + var/selected_type = input("Pick new colour.", "Cable Colour", null, null) as null|anything in possible_cable_coil_colours + set_cable_color(selected_type, usr) // Items usable on a cable coil : // - Wirecutters : cut them duh ! // - Cable coil : merge cables -/obj/item/stack/cable_coil/proc/can_merge(var/obj/item/stack/cable_coil/C) - return color == C.color - -/obj/item/stack/cable_coil/cyborg/can_merge() - return 1 /obj/item/stack/cable_coil/transfer_to(obj/item/stack/cable_coil/S) if(!istype(S)) + world << 1 return - if(!can_merge(S)) - return - ..() /obj/item/stack/cable_coil/use() @@ -880,7 +881,51 @@ obj/structure/cable/proc/cableColor(var/colorC) stacktype = /obj/item/stack/cable_coil color = COLOR_WHITE +/obj/item/stack/cable_coil/silver + stacktype = /obj/item/stack/cable_coil + color = COLOR_SILVER + +/obj/item/stack/cable_coil/gray + stacktype = /obj/item/stack/cable_coil + color = COLOR_GRAY + +/obj/item/stack/cable_coil/black + stacktype = /obj/item/stack/cable_coil + color = COLOR_BLACK + +/obj/item/stack/cable_coil/maroon + stacktype = /obj/item/stack/cable_coil + color = COLOR_MAROON + +/obj/item/stack/cable_coil/olive + stacktype = /obj/item/stack/cable_coil + color = COLOR_OLIVE + +/obj/item/stack/cable_coil/lime + stacktype = /obj/item/stack/cable_coil + color = COLOR_LIME + +/obj/item/stack/cable_coil/teal + stacktype = /obj/item/stack/cable_coil + color = COLOR_TEAL + +/obj/item/stack/cable_coil/navy + stacktype = /obj/item/stack/cable_coil + color = COLOR_NAVY + +/obj/item/stack/cable_coil/purple + stacktype = /obj/item/stack/cable_coil + color = COLOR_PURPLE + +/obj/item/stack/cable_coil/beige + stacktype = /obj/item/stack/cable_coil + color = COLOR_BEIGE + +/obj/item/stack/cable_coil/brown + stacktype = /obj/item/stack/cable_coil + color = COLOR_BROWN + /obj/item/stack/cable_coil/random/New() stacktype = /obj/item/stack/cable_coil - color = pick(COLOR_RED, COLOR_BLUE, COLOR_LIME, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) + color = pick(COLOR_RED, COLOR_BLUE, COLOR_LIME, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN, COLOR_SILVER, COLOR_GRAY, COLOR_BLACK, COLOR_MAROON, COLOR_OLIVE, COLOR_LIME, COLOR_TEAL, COLOR_NAVY, COLOR_PURPLE, COLOR_BEIGE, COLOR_BROWN) ..() \ No newline at end of file diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index f175990a8d..2db78ccd86 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -247,7 +247,7 @@ admin_attack_log(usr, attacker_message="Fired [src]", admin_message="fired a gun ([src]) (MODE: [src.mode_name]) [reflex ? "by reflex" : "manually"].") //update timing - user.setClickCooldown(4) + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) user.setMoveCooldown(move_delay) next_fire_time = world.time + fire_delay diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm index 156083b94a..20fa18b09e 100644 --- a/code/modules/projectiles/guns/energy/stun.dm +++ b/code/modules/projectiles/guns/energy/stun.dm @@ -15,7 +15,7 @@ /obj/item/weapon/gun/energy/taser/mounted/cyborg name = "taser gun" max_shots = 6 - recharge_time = 10 //Time it takes for shots to recharge (in ticks) + recharge_time = 7 //Time it takes for shots to recharge (in ticks) /obj/item/weapon/gun/energy/stunrevolver diff --git a/code/modules/projectiles/projectile/change.dm b/code/modules/projectiles/projectile/change.dm index 9fcc709ba0..b389d39153 100644 --- a/code/modules/projectiles/projectile/change.dm +++ b/code/modules/projectiles/projectile/change.dm @@ -78,7 +78,7 @@ H.set_species(randomize) H.universal_speak = 1 var/datum/preferences/A = new() //Randomize appearance for the human - A.randomize_appearance_for(H) + A.randomize_appearance_and_body_for(H) if(new_mob) for (var/spell/S in M.spell_list) diff --git a/code/modules/projectiles/targeting/targeting_overlay.dm b/code/modules/projectiles/targeting/targeting_overlay.dm index b0577bdf5f..8c87751de7 100644 --- a/code/modules/projectiles/targeting/targeting_overlay.dm +++ b/code/modules/projectiles/targeting/targeting_overlay.dm @@ -23,7 +23,6 @@ owner = newowner loc = null verbs.Cut() - toggle_permission(TARGET_CAN_RADIO) /obj/aiming_overlay/proc/toggle_permission(var/perm) diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index 89aa9b76f3..b2d04233f2 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -383,6 +383,10 @@ if(type == CHEM_TOUCH) var/datum/reagents/R = C.touching return trans_to_holder(R, amount, multiplier, copy) + else if(isxeno(target)) + var/mob/living/simple_animal/xeno/X = target + var/datum/reagents/R = X.reagents + return trans_to_holder(R, amount, multiplier, copy) else var/datum/reagents/R = new /datum/reagents(amount) . = trans_to_holder(R, amount, multiplier, copy) diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm index be96e6665a..469c34a1c1 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm @@ -548,6 +548,20 @@ cup_name = "cup of milk" cup_desc = "White and nutritious goodness!" +/datum/reagent/drink/milk/chocolate + name = "Chocolate Milk" + id = "chocolate_milk" + description = "A delicious mixture of perfectly healthy mix and terrible chocolate." + color = "#74533b" + + glass_icon_state = "glass_brown" + glass_name = "glass of chocolate milk" + glass_desc = "Delciously fattening!" + + cup_icon_state = "cup_brown" + cup_name = "cup of chocolate milk" + cup_desc = "Deliciously fattening!" + /datum/reagent/drink/milk/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) ..() if(alien == IS_DIONA) diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm index 08ec5df6c5..c81a94927c 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Medicine.dm @@ -257,7 +257,7 @@ var/mob/living/carbon/human/H = M var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES] if(istype(E)) - if(E.status & ORGAN_ROBOT) + if(E.robotic >= ORGAN_ROBOT) return if(E.damage > 0) E.damage = max(E.damage - 5 * removed, 0) @@ -277,7 +277,7 @@ if(ishuman(M)) var/mob/living/carbon/human/H = M for(var/obj/item/organ/I in H.internal_organs) - if(I.status & ORGAN_ROBOT) + if(I.robotic >= ORGAN_ROBOT) continue if(I.damage > 0) //Peridaxon heals only non-robotic organs I.damage = max(I.damage - removed, 0) diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm index 0022026a67..bc0072511a 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm @@ -474,42 +474,46 @@ color = "#13BC5E" /datum/reagent/slimetoxin/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.species.name != "Promethean") - M << "Your flesh rapidly mutates!" - H.set_species("Promethean") - H.shapeshifter_set_colour("#05FF9B") - H.verbs -= /mob/living/carbon/human/proc/shapeshifter_select_colour + if(M.isSynthetic()) + return + + var/mob/living/carbon/human/H = M + if(istype(H) && (H.species.flags & NO_SCAN)) + return + + if(M.dna) + if(prob(removed * 0.1)) + randmuti(M) + if(prob(98)) + randmutb(M) + else + randmutg(M) + domutcheck(M, null) + M.UpdateAppearance() + M.apply_effect(16 * removed, IRRADIATE, 0) /datum/reagent/aslimetoxin - name = "Advanced Mutation Toxin" - id = "amutationtoxin" - description = "An advanced corruptive toxin produced by slimes." + name = "Docility Toxin" + id = "docilitytoxin" + description = "A corruptive toxin produced by slimes." reagent_state = LIQUID - color = "#13BC5E" + color = "#FF69B4" /datum/reagent/aslimetoxin/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) // TODO: check if there's similar code anywhere else - if(M.transforming) + if(M.isSynthetic()) return - M << "Your flesh rapidly mutates!" - M.transforming = 1 - M.canmove = 0 - M.icon = null - M.overlays.Cut() - M.invisibility = 101 - for(var/obj/item/W in M) - if(istype(W, /obj/item/weapon/implant)) //TODO: Carn. give implants a dropped() or something - qdel(W) - continue - W.layer = initial(W.layer) - W.loc = M.loc - W.dropped(M) - var/mob/living/carbon/slime/new_mob = new /mob/living/carbon/slime(M.loc) - new_mob.a_intent = "hurt" - new_mob.universal_speak = 1 - if(M.mind) - M.mind.transfer_to(new_mob) - else - new_mob.key = M.key - qdel(M) + + var/mob/living/carbon/human/H = M + if(istype(H) && (H.species.flags & NO_SCAN)) + return + + if(M.dna) + if(prob(removed * 0.1)) + randmuti(M) + if(prob(98)) + randmutb(M) + else + randmutg(M) + domutcheck(M, null) + M.UpdateAppearance() + M.apply_effect(6 * removed, IRRADIATE, 0) diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index 8146069f5c..e9c3df5098 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -998,333 +998,25 @@ /datum/chemical_reaction/slime var/required = null +//Slimed monkeys /datum/chemical_reaction/slime/can_happen(var/datum/reagents/holder) if(holder.my_atom && istype(holder.my_atom, required)) - var/obj/item/slime_extract/T = holder.my_atom - if(T.Uses > 0) - return ..() + return ..() return 0 - -/datum/chemical_reaction/slime/on_reaction(var/datum/reagents/holder) - var/obj/item/slime_extract/T = holder.my_atom - T.Uses-- - if(T.Uses <= 0) - T.visible_message("\icon[T]\The [T]'s power is consumed in the reaction.") - T.name = "used slime extract" - T.desc = "This extract has been used up." - -//Grey -/datum/chemical_reaction/slime/spawn - name = "Slime Spawn" - id = "m_spawn" - result = null - required_reagents = list("phoron" = 1) - result_amount = 1 - required = /obj/item/slime_extract/grey - -/datum/chemical_reaction/slime/spawn/on_reaction(var/datum/reagents/holder) - holder.my_atom.visible_message("Infused with phoron, the core begins to quiver and grow, and soon a new baby slime emerges from it!") - var/mob/living/carbon/slime/S = new /mob/living/carbon/slime - S.loc = get_turf(holder.my_atom) - ..() - -/datum/chemical_reaction/slime/monkey - name = "Slime Monkey" - id = "m_monkey" - result = null - required_reagents = list("blood" = 1) - result_amount = 1 - required = /obj/item/slime_extract/grey - -/datum/chemical_reaction/slime/monkey/on_reaction(var/datum/reagents/holder) - for(var/i = 1, i <= 3, i++) - var /obj/item/weapon/reagent_containers/food/snacks/monkeycube/M = new /obj/item/weapon/reagent_containers/food/snacks/monkeycube - M.loc = get_turf(holder.my_atom) - ..() - -//Green -/datum/chemical_reaction/slime/mutate - name = "Mutation Toxin" - id = "mutationtoxin" - result = "mutationtoxin" - required_reagents = list("phoron" = 1) - result_amount = 1 - required = /obj/item/slime_extract/green - -//Metal -/datum/chemical_reaction/slime/metal - name = "Slime Metal" - id = "m_metal" - result = null - required_reagents = list("phoron" = 1) - result_amount = 1 - required = /obj/item/slime_extract/metal - -/datum/chemical_reaction/slime/metal/on_reaction(var/datum/reagents/holder) - var/obj/item/stack/material/steel/M = new /obj/item/stack/material/steel - M.amount = 15 - M.loc = get_turf(holder.my_atom) - var/obj/item/stack/material/plasteel/P = new /obj/item/stack/material/plasteel - P.amount = 5 - P.loc = get_turf(holder.my_atom) - ..() - -//Gold - removed -/datum/chemical_reaction/slime/crit - name = "Slime Crit" - id = "m_tele" - result = null - required_reagents = list("phoron" = 1) - result_amount = 1 - required = /obj/item/slime_extract/gold - mix_message = "The slime core fizzles disappointingly." - -//Silver -/datum/chemical_reaction/slime/bork - name = "Slime Bork" - id = "m_tele2" - result = null - required_reagents = list("phoron" = 1) - result_amount = 1 - required = /obj/item/slime_extract/silver - -/datum/chemical_reaction/slime/bork/on_reaction(var/datum/reagents/holder) - var/list/borks = typesof(/obj/item/weapon/reagent_containers/food/snacks) - /obj/item/weapon/reagent_containers/food/snacks - playsound(get_turf(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1) - for(var/mob/living/carbon/human/M in viewers(get_turf(holder.my_atom), null)) - if(M.eyecheck() <= 0) - flick("e_flash", M.flash) - - for(var/i = 1, i <= 4 + rand(1,2), i++) - var/chosen = pick(borks) - var/obj/B = new chosen - if(B) - B.loc = get_turf(holder.my_atom) - if(prob(50)) - for(var/j = 1, j <= rand(1, 3), j++) - step(B, pick(NORTH, SOUTH, EAST, WEST)) - ..() - -//Blue -/datum/chemical_reaction/slime/frost - name = "Slime Frost Oil" - id = "m_frostoil" - result = "frostoil" - required_reagents = list("phoron" = 1) - result_amount = 10 - required = /obj/item/slime_extract/blue - -//Dark Blue -/datum/chemical_reaction/slime/freeze - name = "Slime Freeze" - id = "m_freeze" - result = null - required_reagents = list("phoron" = 1) - result_amount = 1 - required = /obj/item/slime_extract/darkblue - mix_message = "The slime extract begins to vibrate violently!" - -/datum/chemical_reaction/slime/freeze/on_reaction(var/datum/reagents/holder) - ..() - sleep(50) - playsound(get_turf(holder.my_atom), 'sound/effects/phasein.ogg', 100, 1) - for(var/mob/living/M in range (get_turf(holder.my_atom), 7)) - M.bodytemperature -= 140 - M << "You feel a chill!" - -//Orange -/datum/chemical_reaction/slime/casp - name = "Slime Capsaicin Oil" - id = "m_capsaicinoil" - result = "capsaicin" - required_reagents = list("blood" = 1) - result_amount = 10 - required = /obj/item/slime_extract/orange - -/datum/chemical_reaction/slime/fire - name = "Slime fire" - id = "m_fire" - result = null - required_reagents = list("phoron" = 1) - result_amount = 1 - required = /obj/item/slime_extract/orange - mix_message = "The slime extract begins to vibrate violently!" - -/datum/chemical_reaction/slime/fire/on_reaction(var/datum/reagents/holder) - ..() - sleep(50) - var/turf/location = get_turf(holder.my_atom.loc) - for(var/turf/simulated/floor/target_tile in range(0, location)) - target_tile.assume_gas("phoron", 25, 1400) - spawn (0) - target_tile.hotspot_expose(700, 400) - -//Yellow -/datum/chemical_reaction/slime/overload - name = "Slime EMP" - id = "m_emp" - result = null - required_reagents = list("blood" = 1) - result_amount = 1 - required = /obj/item/slime_extract/yellow - -/datum/chemical_reaction/slime/overload/on_reaction(var/datum/reagents/holder, var/created_volume) - ..() - empulse(get_turf(holder.my_atom), 3, 7) - -/datum/chemical_reaction/slime/cell - name = "Slime Powercell" - id = "m_cell" - result = null - required_reagents = list("phoron" = 1) - result_amount = 1 - required = /obj/item/slime_extract/yellow - -/datum/chemical_reaction/slime/cell/on_reaction(var/datum/reagents/holder, var/created_volume) - var/obj/item/weapon/cell/slime/P = new /obj/item/weapon/cell/slime - P.loc = get_turf(holder.my_atom) - -/datum/chemical_reaction/slime/glow - name = "Slime Glow" - id = "m_glow" - result = null - required_reagents = list("water" = 1) - result_amount = 1 - required = /obj/item/slime_extract/yellow - mix_message = "The contents of the slime core harden and begin to emit a warm, bright light." - -/datum/chemical_reaction/slime/glow/on_reaction(var/datum/reagents/holder, var/created_volume) - ..() - var/obj/item/device/flashlight/slime/F = new /obj/item/device/flashlight/slime - F.loc = get_turf(holder.my_atom) - -//Purple -/datum/chemical_reaction/slime/psteroid - name = "Slime Steroid" - id = "m_steroid" - result = null - required_reagents = list("phoron" = 1) - result_amount = 1 - required = /obj/item/slime_extract/purple - -/datum/chemical_reaction/slime/psteroid/on_reaction(var/datum/reagents/holder, var/created_volume) - ..() - var/obj/item/weapon/slimesteroid/P = new /obj/item/weapon/slimesteroid - P.loc = get_turf(holder.my_atom) - -/datum/chemical_reaction/slime/jam - name = "Slime Jam" - id = "m_jam" - result = "slimejelly" - required_reagents = list("sugar" = 1) - result_amount = 10 - required = /obj/item/slime_extract/purple - -//Dark Purple -/datum/chemical_reaction/slime/plasma - name = "Slime Plasma" - id = "m_plasma" - result = null - required_reagents = list("phoron" = 1) - result_amount = 1 - required = /obj/item/slime_extract/darkpurple - -/datum/chemical_reaction/slime/plasma/on_reaction(var/datum/reagents/holder) - ..() - var/obj/item/stack/material/phoron/P = new /obj/item/stack/material/phoron - P.amount = 10 - P.loc = get_turf(holder.my_atom) - -//Red -/datum/chemical_reaction/slime/glycerol - name = "Slime Glycerol" - id = "m_glycerol" - result = "glycerol" - required_reagents = list("phoron" = 1) - result_amount = 8 - required = /obj/item/slime_extract/red - -/datum/chemical_reaction/slime/bloodlust - name = "Bloodlust" - id = "m_bloodlust" - result = null - required_reagents = list("blood" = 1) - result_amount = 1 - required = /obj/item/slime_extract/red - -/datum/chemical_reaction/slime/bloodlust/on_reaction(var/datum/reagents/holder) - ..() - for(var/mob/living/carbon/slime/slime in viewers(get_turf(holder.my_atom), null)) - slime.rabid = 1 - slime.visible_message("The [slime] is driven into a frenzy!") - -//Pink -/datum/chemical_reaction/slime/ppotion - name = "Slime Potion" - id = "m_potion" - result = null - required_reagents = list("phoron" = 1) - result_amount = 1 - required = /obj/item/slime_extract/pink - -/datum/chemical_reaction/slime/ppotion/on_reaction(var/datum/reagents/holder) - ..() - var/obj/item/weapon/slimepotion/P = new /obj/item/weapon/slimepotion - P.loc = get_turf(holder.my_atom) - -//Black -/datum/chemical_reaction/slime/mutate2 - name = "Advanced Mutation Toxin" - id = "mutationtoxin2" - result = "amutationtoxin" - required_reagents = list("phoron" = 1) - result_amount = 1 - required = /obj/item/slime_extract/black - -//Oil -/datum/chemical_reaction/slime/explosion - name = "Slime Explosion" - id = "m_explosion" - result = null - required_reagents = list("phoron" = 1) - result_amount = 1 - required = /obj/item/slime_extract/oil - mix_message = "The slime extract begins to vibrate violently!" - -/datum/chemical_reaction/slime/explosion/on_reaction(var/datum/reagents/holder) - ..() - sleep(50) - explosion(get_turf(holder.my_atom), 1, 3, 6) - -//Light Pink -/datum/chemical_reaction/slime/potion2 - name = "Slime Potion 2" - id = "m_potion2" - result = null - result_amount = 1 - required = /obj/item/slime_extract/lightpink - required_reagents = list("phoron" = 1) - -/datum/chemical_reaction/slime/potion2/on_reaction(var/datum/reagents/holder) - ..() - var/obj/item/weapon/slimepotion2/P = new /obj/item/weapon/slimepotion2 - P.loc = get_turf(holder.my_atom) - -//Adamantine + /datum/chemical_reaction/slime/golem - name = "Slime Golem" - id = "m_golem" + name = "Prometheans" + id = "m_promethean" result = null - required_reagents = list("phoron" = 1) + required_reagents = list("mutationtoxin" = 1) result_amount = 1 - required = /obj/item/slime_extract/adamantine + required = /obj/item/weapon/reagent_containers/food/snacks/monkeycube /datum/chemical_reaction/slime/golem/on_reaction(var/datum/reagents/holder) - ..() - var/obj/effect/golemrune/Z = new /obj/effect/golemrune - Z.loc = get_turf(holder.my_atom) - Z.announce_to_ghosts() - + var/location = get_turf(holder.my_atom) + new /obj/item/slime_cube(location) + qdel(holder.my_atom) + /* Food */ /datum/chemical_reaction/tofu diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index f98d90b248..2c8c853ebc 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -25,11 +25,6 @@ /obj/item/weapon/reagent_containers/attack_self(mob/user as mob) return -/obj/item/weapon/reagent_containers/attack(mob/M as mob, mob/user as mob, def_zone) - if(can_operate(M))//Checks if mob is lying down on table for surgery - if(do_surgery(M, user, src)) - return - /obj/item/weapon/reagent_containers/afterattack(obj/target, mob/user, flag) return @@ -106,6 +101,7 @@ user << "\The [blocked] is in the way!" return + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) //puts a limit on how fast people can eat/drink things self_feed_message(user) reagents.trans_to_mob(user, issmall(user) ? ceil(amount_per_transfer_from_this/2) : amount_per_transfer_from_this, CHEM_INGEST) feed_sound(user) @@ -123,6 +119,7 @@ other_feed_message_start(user, target) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) if(!do_mob(user, target)) return diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm index 2c8396deee..ce377c8709 100644 --- a/code/modules/reagents/reagent_containers/borghydro.dm +++ b/code/modules/reagents/reagent_containers/borghydro.dm @@ -13,15 +13,15 @@ var/charge_tick = 0 var/recharge_time = 5 //Time it takes for shots to recharge (in seconds) - var/list/reagent_ids = list("tricordrazine", "inaprovaline", "spaceacillin") + var/list/reagent_ids = list("tricordrazine", "inaprovaline", "tramadol", "dexalin" ,"spaceacillin") var/list/reagent_volumes = list() var/list/reagent_names = list() /obj/item/weapon/reagent_containers/borghypo/surgeon - reagent_ids = list("bicaridine", "inaprovaline", "dexalin") + reagent_ids = list("tricordrazine", "inaprovaline", "tramadol", "dexalin" ,"spaceacillin") /obj/item/weapon/reagent_containers/borghypo/crisis - reagent_ids = list("tricordrazine", "inaprovaline", "tramadol") + reagent_ids = list("tricordrazine", "inaprovaline", "tramadol", "dexalin" ,"spaceacillin") /obj/item/weapon/reagent_containers/borghypo/New() ..() @@ -65,7 +65,7 @@ if(!affected) user << "\The [H] is missing that limb!" return - else if(affected.status & ORGAN_ROBOT) + else if(affected.robotic >= ORGAN_ROBOT) user << "You cannot inject a robotic limb." return diff --git a/code/modules/reagents/reagent_containers/food/drinks.dm b/code/modules/reagents/reagent_containers/food/drinks.dm index c934343e77..dea0da4180 100644 --- a/code/modules/reagents/reagent_containers/food/drinks.dm +++ b/code/modules/reagents/reagent_containers/food/drinks.dm @@ -23,6 +23,9 @@ flags |= OPENCONTAINER /obj/item/weapon/reagent_containers/food/drinks/attack(mob/M as mob, mob/user as mob, def_zone) + if(force && !(flags & NOBLUDGEON) && user.a_intent == I_HURT) + return ..() + if(standard_feed_mob(user, M)) return @@ -93,10 +96,6 @@ volume = 150 flags = CONDUCT | OPENCONTAINER -/obj/item/weapon/reagent_containers/food/drinks/golden_cup/tournament_26_06_2011 - desc = "A golden cup. It will be presented to a winner of tournament 26 june and name of the winner will be graved on it." - - ///////////////////////////////////////////////Drinks //Notes by Darem: Drinks are simply containers that start preloaded. Unlike condiments, the contents can be ingested directly // rather then having to add it to something else first. They should only contain liquids. They have a default container size of 50. @@ -123,6 +122,23 @@ ..() reagents.add_reagent("soymilk", 50) +/obj/item/weapon/reagent_containers/food/drinks/milk/smallcarton + name = "Small Carton of Milk" + volume = 30 + icon_state = "mini-milk" +/obj/item/weapon/reagent_containers/food/drinks/milk/smallcarton/New() + ..() + reagents.add_reagent("milk", 30) + +/obj/item/weapon/reagent_containers/food/drinks/milk/smallcarton/chocolate + name = "Small Carton of Chocolate Milk" + desc = "It's milk! This one is in delicious chocolate flavour." + +/obj/item/weapon/reagent_containers/food/drinks/milk/smallcarton/chocolate/New() + ..() + reagents.add_reagent("chocolate_milk", 30) + + /obj/item/weapon/reagent_containers/food/drinks/coffee name = "Robust Coffee" desc = "Careful, the beverage you're about to enjoy is extremely hot." diff --git a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm index a9e55b26fe..c0189dd1c6 100644 --- a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm +++ b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm @@ -32,7 +32,7 @@ var/throw_dist = get_dist(throw_source, loc) if(speed >= throw_speed && smash_check(throw_dist)) //not as reliable as smashing directly if(reagents) - hit_atom.visible_message("The contents of the [src] splash all over [hit_atom]!") + hit_atom.visible_message("The contents of \the [src] splash all over [hit_atom]!") reagents.splash(hit_atom, reagents.total_volume) src.smash(loc, hit_atom) @@ -40,7 +40,7 @@ if(!isGlass || !smash_duration) return 0 - var/list/chance_table = list(90, 90, 85, 85, 60, 35, 15) //starting from distance 0 + var/list/chance_table = list(100, 95, 90, 85, 75, 55, 35) //starting from distance 0 var/idx = max(distance + 1, 1) //since list indices start at 1 if(idx > chance_table.len) return 0 @@ -137,59 +137,34 @@ else set_light(0) -/obj/item/weapon/reagent_containers/food/drinks/bottle/attack(mob/living/target as mob, mob/living/user as mob) - if(!target) +/obj/item/weapon/reagent_containers/food/drinks/bottle/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone) + var/blocked = ..() + + if(user.a_intent != I_HURT) return - - if(user.a_intent != I_HURT || !isGlass) - return ..() - - var/affecting = user.zone_sel.selecting //Find what the player is aiming at - - var/armor_block = 0 //Get the target's armour values for normal attack damage. - var/armor_duration = 0 //The more force the bottle has, the longer the duration. - - //Calculating duration and calculating damage. - armor_block = target.run_armor_check(affecting, "melee") - - //force will counteract armour, but will never increase duration - armor_duration = smash_duration + min(0, force - target.getarmor(affecting, "melee") + 10) - - //Apply the damage! - target.apply_damage(force, BRUTE, affecting, armor_block, sharp=0) + if(!smash_check(1)) + return //won't always break on the first hit // You are going to knock someone out for longer if they are not wearing a helmet. - var/do_smash = smash_check(1) //won't always break on the first hit - var/mob/living/carbon/human/H = target - if(do_smash && istype(H) && H.headcheck(affecting)) - //Display an attack message. - var/obj/item/organ/O = H.get_organ(affecting) - user.visible_message("[user] smashes [src] into [H]'s [O.name]!") - //Weaken the target for the duration that we calculated and divide it by 5. - if(armor_duration) - target.apply_effect(min(armor_duration, 5) , WEAKEN, armor_block) // Never weaken more than a flash! + var/weaken_duration = 0 + if(blocked < 100) + weaken_duration = smash_duration + min(0, force - target.getarmor(hit_zone, "melee") + 10) + + if(hit_zone == "head" && istype(target, /mob/living/carbon/)) + user.visible_message("\The [user] smashes [src] over [target]'s head!") + if(weaken_duration) + target.apply_effect(min(weaken_duration, 5), WEAKEN, blocked) // Never weaken more than a flash! else - //Default attack message and don't weaken the target. - for(var/mob/O in viewers(user, null)) - if(target != user) O.show_message(text("\red [target] has been attacked with a bottle of [src.name], by [user]!"), 1) - else O.show_message(text("\red [target] has attacked \himself with a bottle of [src.name]!"), 1) + user.visible_message("\The [user] smashes [src] into [target]!") - //Attack logs - user.attack_log += text("\[[time_stamp()]\] Has attacked [target.name] ([target.ckey]) with a bottle!") - target.attack_log += text("\[[time_stamp()]\] Has been smashed with a bottle by [user.name] ([user.ckey])") - msg_admin_attack("[user.name] ([user.ckey]) attacked [target.name] ([target.ckey]) with a bottle. (INTENT: [uppertext(user.a_intent)]) (JMP)") + //The reagents in the bottle splash all over the target, thanks for the idea Nodrak + if(reagents) + user.visible_message("The contents of \the [src] splash all over [target]!") + reagents.splash(target, reagents.total_volume) - if(do_smash) - //The reagents in the bottle splash all over the target, thanks for the idea Nodrak - if(reagents) - user.visible_message("The contents of the [src] splash all over [target]!") - reagents.splash(target, reagents.total_volume) - - //Finally, smash the bottle. This kills (qdel) the bottle. - var/obj/item/weapon/broken_bottle/B = src.smash(target.loc, target) - user.put_in_active_hand(B) - - return + //Finally, smash the bottle. This kills (qdel) the bottle. + var/obj/item/weapon/broken_bottle/B = smash(target.loc, target) + user.put_in_active_hand(B) //Keeping this here for now, I'll ask if I should keep it here. /obj/item/weapon/broken_bottle @@ -507,4 +482,4 @@ /obj/item/weapon/reagent_containers/food/drinks/bottle/small/ale/New() ..() - reagents.add_reagent("ale", 30) \ No newline at end of file + reagents.add_reagent("ale", 30) diff --git a/code/modules/reagents/reagent_containers/food/drinks/drinkingglass.dm b/code/modules/reagents/reagent_containers/food/drinks/drinkingglass.dm index cdf495405f..b10feb2b59 100644 --- a/code/modules/reagents/reagent_containers/food/drinks/drinkingglass.dm +++ b/code/modules/reagents/reagent_containers/food/drinks/drinkingglass.dm @@ -133,3 +133,46 @@ name = "shot glass of " + reagents.get_master_reagent_name() //No matter what, the glass will tell you the reagent's name. Might be too abusable in the future. else name = "shot glass" + +/obj/item/weapon/reagent_containers/food/drinks/drinkingglass/fitnessflask + name = "fitness shaker" + desc = "Big enough to contain enough protein to get perfectly swole. Don't mind the bits." + icon_state = "fitness-cup_black" + volume = 100 + matter = list("plastic" = 2000) + +/obj/item/weapon/reagent_containers/food/drinks/drinkingglass/fitnessflask/New() + ..() + icon_state = pick("fitness-cup_black", "fitness-cup_red", "fitness-cup_black") + +/obj/item/weapon/reagent_containers/food/drinks/drinkingglass/fitnessflask/on_reagent_change() + overlays.Cut() + + if(reagents.total_volume) + var/image/filling = image('icons/obj/reagentfillings.dmi', src, "fitness-cup10") + + switch(reagents.total_volume) + if(0 to 10) filling.icon_state = "fitness-cup10" + if(11 to 20) filling.icon_state = "fitness-cup20" + if(21 to 29) filling.icon_state = "fitness-cup30" + if(30 to 39) filling.icon_state = "fitness-cup40" + if(40 to 49) filling.icon_state = "fitness-cup50" + if(50 to 59) filling.icon_state = "fitness-cup60" + if(60 to 69) filling.icon_state = "fitness-cup70" + if(70 to 79) filling.icon_state = "fitness-cup80" + if(80 to 89) filling.icon_state = "fitness-cup90" + if(90 to INFINITY) filling.icon_state = "fitness-cup100" + + filling.color += reagents.get_color() + overlays += filling + +/obj/item/weapon/reagent_containers/food/drinks/drinkingglass/fitnessflask/proteinshake + name = "protein shake" + +/obj/item/weapon/reagent_containers/food/drinks/drinkingglass/fitnessflask/proteinshake/New() + ..() + reagents.add_reagent("nutriment", 30) + reagents.add_reagent("iron", 10) + reagents.add_reagent("protein", 15) + reagents.add_reagent("water", 45) + on_reagent_change() \ No newline at end of file diff --git a/code/modules/reagents/reagent_containers/food/snacks.dm b/code/modules/reagents/reagent_containers/food/snacks.dm index 98ce8751aa..45b0eb97b8 100644 --- a/code/modules/reagents/reagent_containers/food/snacks.dm +++ b/code/modules/reagents/reagent_containers/food/snacks.dm @@ -41,6 +41,8 @@ return 0 if(istype(M, /mob/living/carbon)) + //TODO: replace with standard_feed_mob() call. + var/fullness = M.nutrition + (M.reagents.get_reagent_amount("nutriment") * 25) if(M == user) //If you're eating it yourself if(istype(M,/mob/living/carbon/human)) @@ -53,6 +55,7 @@ user << "\The [blocked] is in the way!" return + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) //puts a limit on how fast people can eat/drink things if (fullness <= 50) M << "You hungrily chew out a piece of [src] and gobble it!" if (fullness > 50 && fullness <= 150) @@ -83,6 +86,7 @@ user.visible_message("[user] cannot force anymore of [src] down [M]'s throat.") return 0 + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) if(!do_mob(user, M)) return M.attack_log += text("\[[time_stamp()]\] Has been fed [src.name] by [user.name] ([user.ckey]) Reagents: [reagentlist(src)]") @@ -273,6 +277,19 @@ reagents.add_reagent("sugar", 3) bitesize = 2 +/obj/item/weapon/reagent_containers/food/snacks/candy/proteinbar + name = "protein bar" + desc = "SwoleMAX brand protein bars, guaranteed to get you feeling perfectly overconfident." + icon_state = "proteinbar" + trash = /obj/item/trash/candy/proteinbar + +/obj/item/weapon/reagent_containers/food/snacks/candy/proteinbar/New() + ..() + reagents.add_reagent("nutriment", 9) + reagents.add_reagent("protein", 4) + reagents.add_reagent("sugar", 4) + bitesize = 6 + /obj/item/weapon/reagent_containers/food/snacks/candy/donor name = "Donor Candy" desc = "A little treat for blood donors." diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index 44ea628eca..4eb927444e 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -41,7 +41,8 @@ /obj/machinery/smartfridge/, /obj/machinery/biogenerator, /obj/structure/frame, - /obj/machinery/radiocarbon_spectrometer + /obj/machinery/radiocarbon_spectrometer, + /obj/machinery/xenobio2/manualinjector ) /obj/item/weapon/reagent_containers/glass/New() diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 7632f8456f..884afc5ed8 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -33,10 +33,11 @@ if(!affected) user << "\The [H] is missing that limb!" return - else if(affected.status & ORGAN_ROBOT) + else if(affected.robotic >= ORGAN_ROBOT) user << "You cannot inject a robotic limb." return + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) user << "You inject [M] with [src]." M << "You feel a tiny prick!" diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index 89598898f8..def1ac7f81 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -12,12 +12,15 @@ slot_flags = SLOT_EARS volume = 60 + + /obj/item/weapon/reagent_containers/pill/New() ..() if(!icon_state) icon_state = "pill[rand(1, 20)]" /obj/item/weapon/reagent_containers/pill/attack(mob/M as mob, mob/user as mob, def_zone) + if(M == user) if(istype(M, /mob/living/carbon/human)) @@ -50,6 +53,7 @@ user.visible_message("[user] attempts to force [M] to swallow \the [src].") + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) if(!do_mob(user, M)) return @@ -294,3 +298,12 @@ /obj/item/weapon/reagent_containers/pill/spaceacillin/New() ..() reagents.add_reagent("spaceacillin", 15) + +/obj/item/weapon/reagent_containers/pill/diet + name = "diet pill" + desc = "Guaranteed to get you slim!" + icon_state = "pill9" + +/obj/item/weapon/reagent_containers/pill/diet/New() + ..() + reagents.add_reagent("lipozine", 2) \ No newline at end of file diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 7c291f1906..09ae98eb90 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -149,7 +149,7 @@ if(!affected) user << "\The [H] is missing that limb!" return - else if(affected.status & ORGAN_ROBOT) + else if(affected.robotic >= ORGAN_ROBOT) user << "You cannot inject a robotic limb." return @@ -175,6 +175,8 @@ else user.visible_message("[user] begins hunting for an injection port on [target]'s suit!") + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) + if(!do_mob(user, target, injtime)) return @@ -344,3 +346,12 @@ reagents.add_reagent("chloralhydrate", 50) mode = SYRINGE_INJECT update_icon() + +/obj/item/weapon/reagent_containers/syringe/steroid + name = "Syringe (anabolic steroids)" + desc = "Contains drugs for muscle growth." + +/obj/item/weapon/reagent_containers/syringe/steroid/New() + ..() + reagents.add_reagent("adrenaline",5) + reagents.add_reagent("hyperzine",10) diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index 05b8a82f2f..5c1bf634c8 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -206,60 +206,6 @@ other types of metals and chemistry for reagents). build_path = /obj/item/weapon/storage/part_replacer sort_string = "CBAAA" -/datum/design/item/powercell - build_type = PROTOLATHE | MECHFAB - -/datum/design/item/powercell/AssembleDesignName() - name = "Power cell model ([item_name])" - -/datum/design/item/powercell/AssembleDesignDesc() - if(build_path) - var/obj/item/weapon/cell/C = build_path - desc = "Allows the construction of power cells that can hold [initial(C.maxcharge)] units of energy." - -/datum/design/item/powercell/Fabricate() - var/obj/item/weapon/cell/C = ..() - C.charge = 0 //shouldn't produce power out of thin air. - return C - -/datum/design/item/powercell/basic - name = "basic" - build_type = PROTOLATHE | MECHFAB - id = "basic_cell" - req_tech = list(TECH_POWER = 1) - materials = list(DEFAULT_WALL_MATERIAL = 700, "glass" = 50) - build_path = /obj/item/weapon/cell - category = "Misc" - sort_string = "DAAAA" - -/datum/design/item/powercell/high - name = "high-capacity" - build_type = PROTOLATHE | MECHFAB - id = "high_cell" - req_tech = list(TECH_POWER = 2) - materials = list(DEFAULT_WALL_MATERIAL = 700, "glass" = 60) - build_path = /obj/item/weapon/cell/high - category = "Misc" - sort_string = "DAAAB" - -/datum/design/item/powercell/super - name = "super-capacity" - id = "super_cell" - req_tech = list(TECH_POWER = 3, TECH_MATERIAL = 2) - materials = list(DEFAULT_WALL_MATERIAL = 700, "glass" = 70) - build_path = /obj/item/weapon/cell/super - category = "Misc" - sort_string = "DAAAC" - -/datum/design/item/powercell/hyper - name = "hyper-capacity" - id = "hyper_cell" - req_tech = list(TECH_POWER = 5, TECH_MATERIAL = 4) - materials = list(DEFAULT_WALL_MATERIAL = 400, "gold" = 150, "silver" = 150, "glass" = 70) - build_path = /obj/item/weapon/cell/hyper - category = "Misc" - sort_string = "DAAAD" - /datum/design/item/hud materials = list(DEFAULT_WALL_MATERIAL = 50, "glass" = 50) @@ -651,7 +597,7 @@ other types of metals and chemistry for reagents). name = "Positronic brain" id = "posibrain" req_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 6, TECH_BLUESPACE = 2, TECH_DATA = 4) - build_type = PROTOLATHE | MECHFAB + build_type = PROTOLATHE | PROSFAB materials = list(DEFAULT_WALL_MATERIAL = 2000, "glass" = 1000, "silver" = 1000, "gold" = 500, "phoron" = 500, "diamond" = 100) build_path = /obj/item/device/mmi/digital/posibrain category = "Misc" @@ -661,7 +607,7 @@ other types of metals and chemistry for reagents). name = "Man-machine interface" id = "mmi" req_tech = list(TECH_DATA = 2, TECH_BIO = 3) - build_type = PROTOLATHE | MECHFAB + build_type = PROTOLATHE | PROSFAB materials = list(DEFAULT_WALL_MATERIAL = 1000, "glass" = 500) build_path = /obj/item/device/mmi category = "Misc" @@ -671,7 +617,7 @@ other types of metals and chemistry for reagents). name = "Radio-enabled man-machine interface" id = "mmi_radio" req_tech = list(TECH_DATA = 2, TECH_BIO = 4) - build_type = PROTOLATHE | MECHFAB + build_type = PROTOLATHE | PROSFAB materials = list(DEFAULT_WALL_MATERIAL = 1200, "glass" = 500) build_path = /obj/item/device/mmi/radio_enabled category = "Misc" @@ -756,6 +702,13 @@ CIRCUITS BELOW build_path = /obj/item/weapon/circuitboard/arcade/orion_trail sort_string = "MAAAA" +/datum/design/circuit/jukebox + name = "jukebox" + id = "jukebox" + req_tech = list(TECH_MAGNET = 2, TECH_DATA = 1) + build_path = /obj/item/weapon/circuitboard/jukebox + sort_string = "MAAAB" + /datum/design/circuit/seccamera name = "security camera monitor" id = "seccamera" @@ -939,6 +892,13 @@ CIRCUITS BELOW build_path = /obj/item/weapon/circuitboard/mechfab sort_string = "HABAE" +/datum/design/circuit/prosfab + name = "prosthetics fabricator" + id = "prosfab" + req_tech = list(TECH_DATA = 3, TECH_ENGINEERING = 3) + build_path = /obj/item/weapon/circuitboard/prosthetics + sort_string = "HABAF" + /datum/design/circuit/mech_recharger name = "mech recharger" id = "mech_recharger" diff --git a/code/modules/research/mechfab_designs.dm b/code/modules/research/mechfab_designs.dm index 85ace41452..35f064321c 100644 --- a/code/modules/research/mechfab_designs.dm +++ b/code/modules/research/mechfab_designs.dm @@ -1,101 +1,8 @@ /datum/design/item/mechfab build_type = MECHFAB - category = "Misc" + category = "Other" req_tech = list(TECH_MATERIAL = 1) -/datum/design/item/mechfab/robot - category = "Robot" - -//if the fabricator is a mech fab pass the manufacturer info over to the robot part constructor -/datum/design/item/mechfab/robot/Fabricate(var/newloc, var/fabricator) - if(istype(fabricator, /obj/machinery/mecha_part_fabricator)) - var/obj/machinery/mecha_part_fabricator/mechfab = fabricator - return new build_path(newloc, mechfab.manufacturer) - return ..() - -/datum/design/item/mechfab/robot/exoskeleton - name = "Robot exoskeleton" - id = "robot_exoskeleton" - build_path = /obj/item/robot_parts/robot_suit - time = 50 - materials = list(DEFAULT_WALL_MATERIAL = 50000) - -/datum/design/item/mechfab/robot/torso - name = "Robot torso" - id = "robot_torso" - build_path = /obj/item/robot_parts/chest - time = 35 - materials = list(DEFAULT_WALL_MATERIAL = 40000) - -/datum/design/item/mechfab/robot/head - name = "Robot head" - id = "robot_head" - build_path = /obj/item/robot_parts/head - time = 35 - materials = list(DEFAULT_WALL_MATERIAL = 25000) - -/datum/design/item/mechfab/robot/l_arm - name = "Robot left arm" - id = "robot_l_arm" - build_path = /obj/item/robot_parts/l_arm - time = 20 - materials = list(DEFAULT_WALL_MATERIAL = 18000) - -/datum/design/item/mechfab/robot/r_arm - name = "Robot right arm" - id = "robot_r_arm" - build_path = /obj/item/robot_parts/r_arm - time = 20 - materials = list(DEFAULT_WALL_MATERIAL = 18000) - -/datum/design/item/mechfab/robot/l_leg - name = "Robot left leg" - id = "robot_l_leg" - build_path = /obj/item/robot_parts/l_leg - time = 20 - materials = list(DEFAULT_WALL_MATERIAL = 15000) - -/datum/design/item/mechfab/robot/r_leg - name = "Robot right leg" - id = "robot_r_leg" - build_path = /obj/item/robot_parts/r_leg - time = 20 - materials = list(DEFAULT_WALL_MATERIAL = 15000) - -/datum/design/item/mechfab/robot/component - time = 20 - materials = list(DEFAULT_WALL_MATERIAL = 5000) - -/datum/design/item/mechfab/robot/component/binary_communication_device - name = "Binary communication device" - id = "binary_communication_device" - build_path = /obj/item/robot_parts/robot_component/binary_communication_device - -/datum/design/item/mechfab/robot/component/radio - name = "Radio" - id = "radio" - build_path = /obj/item/robot_parts/robot_component/radio - -/datum/design/item/mechfab/robot/component/actuator - name = "Actuator" - id = "actuator" - build_path = /obj/item/robot_parts/robot_component/actuator - -/datum/design/item/mechfab/robot/component/diagnosis_unit - name = "Diagnosis unit" - id = "diagnosis_unit" - build_path = /obj/item/robot_parts/robot_component/diagnosis_unit - -/datum/design/item/mechfab/robot/component/camera - name = "Camera" - id = "camera" - build_path = /obj/item/robot_parts/robot_component/camera - -/datum/design/item/mechfab/robot/component/armour - name = "Armour plating" - id = "armour" - build_path = /obj/item/robot_parts/robot_component/armour - /datum/design/item/mechfab/ripley category = "Ripley" @@ -316,60 +223,6 @@ time = 60 materials = list(DEFAULT_WALL_MATERIAL = 50000, "uranium" = 10000) -/datum/design/item/robot_upgrade - build_type = MECHFAB - time = 12 - materials = list(DEFAULT_WALL_MATERIAL = 10000) - category = "Cyborg Upgrade Modules" - -/datum/design/item/robot_upgrade/rename - name = "Rename module" - desc = "Used to rename a cyborg." - id = "borg_rename_module" - build_path = /obj/item/borg/upgrade/rename - -/datum/design/item/robot_upgrade/reset - name = "Reset module" - desc = "Used to reset a cyborg's module. Destroys any other upgrades applied to the robot." - id = "borg_reset_module" - build_path = /obj/item/borg/upgrade/reset - -/datum/design/item/robot_upgrade/restart - name = "Emergency restart module" - desc = "Used to force a restart of a disabled-but-repaired robot, bringing it back online." - id = "borg_restart_module" - materials = list(DEFAULT_WALL_MATERIAL = 60000, "glass" = 5000) - build_path = /obj/item/borg/upgrade/restart - -/datum/design/item/robot_upgrade/vtec - name = "VTEC module" - desc = "Used to kick in a robot's VTEC systems, increasing their speed." - id = "borg_vtec_module" - materials = list(DEFAULT_WALL_MATERIAL = 80000, "glass" = 6000, "gold" = 5000) - build_path = /obj/item/borg/upgrade/vtec - -/datum/design/item/robot_upgrade/tasercooler - name = "Rapid taser cooling module" - desc = "Used to cool a mounted taser, increasing the potential current in it and thus its recharge rate." - id = "borg_taser_module" - materials = list(DEFAULT_WALL_MATERIAL = 80000, "glass" = 6000, "gold" = 2000, "diamond" = 500) - build_path = /obj/item/borg/upgrade/tasercooler - -/datum/design/item/robot_upgrade/jetpack - name = "Jetpack module" - desc = "A carbon dioxide jetpack suitable for low-gravity mining operations." - id = "borg_jetpack_module" - materials = list(DEFAULT_WALL_MATERIAL = 10000, "phoron" = 15000, "uranium" = 20000) - build_path = /obj/item/borg/upgrade/jetpack - -/datum/design/item/robot_upgrade/syndicate - name = "Illegal upgrade" - desc = "Allows for the construction of lethal upgrades for cyborgs." - id = "borg_syndicate_module" - req_tech = list(TECH_COMBAT = 4, TECH_ILLEGAL = 3) - materials = list(DEFAULT_WALL_MATERIAL = 10000, "glass" = 15000, "diamond" = 10000) - build_path = /obj/item/borg/upgrade/syndicate - /datum/design/item/mecha_tracking name = "Exosuit tracking beacon" build_type = MECHFAB @@ -407,7 +260,7 @@ name = "Cable layer" id = "mech_cable_layer" build_path = /obj/item/mecha_parts/mecha_equipment/tool/cable_layer - + /datum/design/item/mecha/flaregun name = "Flare launcher" id = "mecha_flare_gun" diff --git a/code/modules/research/prosfab_designs.dm b/code/modules/research/prosfab_designs.dm new file mode 100644 index 0000000000..c12434536b --- /dev/null +++ b/code/modules/research/prosfab_designs.dm @@ -0,0 +1,297 @@ +/datum/design/item/prosfab + build_type = PROSFAB + category = "Misc" + req_tech = list(TECH_MATERIAL = 1) + +/datum/design/item/prosfab/pros + category = "Prosthetics" + +// Make new external organs and make 'em robotish +/datum/design/item/prosfab/pros/Fabricate(var/newloc, var/fabricator) + if(istype(fabricator, /obj/machinery/pros_fabricator)) + var/obj/machinery/pros_fabricator/prosfab = fabricator + var/obj/item/organ/O = new build_path(newloc) + O.species = all_species["Human"] + O.robotize(prosfab.manufacturer) + O.dna = new/datum/dna() //Uuughhhh... why do I have to do this? + O.dna.ResetUI() + O.dna.ResetSE() + spawn(10) //Limbs love to flop around. Who am I to deny them? + O.dir = 2 + return O + return ..() + +// Deep Magic for the torso since it needs to be a new mob +/datum/design/item/prosfab/pros/torso/Fabricate(var/newloc, var/fabricator) + if(istype(fabricator, /obj/machinery/pros_fabricator)) + var/obj/machinery/pros_fabricator/prosfab = fabricator + var/mob/living/carbon/human/H = new(newloc,"Human") + H.stat = DEAD + H.gender = gender + for(var/obj/item/organ/external/EO in H.organs) + if(EO.organ_tag == BP_TORSO || EO.organ_tag == BP_GROIN) + continue //Roboticizing a torso does all the children and wastes time, do it later + else + EO.remove_rejuv() + + for(var/obj/item/organ/external/O in H.organs) + O.species = all_species["Human"] + O.robotize(prosfab.manufacturer) + O.dna = new/datum/dna() + O.dna.ResetUI() + O.dna.ResetSE() + + H.real_name = "Synthmorph #[rand(100,999)]" + H.name = H.real_name + H.dir = 2 + return H + +//////////////////// Prosthetics //////////////////// +/datum/design/item/prosfab/pros/torso + time = 35 + materials = list(DEFAULT_WALL_MATERIAL = 60000, "glass" = 10000, "plasteel" = 2000) + req_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 3, TECH_DATA = 3) + var/gender = MALE + +/datum/design/item/prosfab/pros/torso/male + name = "FBP torso (M)" + id = "pros_torso_m" + build_path = /obj/item/organ/external/chest + gender = MALE + +/obj/item/organ/external/chest/f //To satisfy Travis. :| + +/datum/design/item/prosfab/pros/torso/female + name = "FBP torso (F)" + id = "pros_torso_f" + build_path = /obj/item/organ/external/chest/f + gender = FEMALE + +/datum/design/item/prosfab/pros/head + name = "Prosthetic head" + id = "pros_head" + build_path = /obj/item/organ/external/head + time = 30 + materials = list(DEFAULT_WALL_MATERIAL = 25000, "glass" = 5000, "plasteel" = 1000) + req_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 3, TECH_DATA = 3) + +/datum/design/item/prosfab/pros/l_arm + name = "Prosthetic left arm" + id = "pros_l_arm" + build_path = /obj/item/organ/external/arm + time = 20 + materials = list(DEFAULT_WALL_MATERIAL = 18000) + +/datum/design/item/prosfab/pros/l_hand + name = "Prosthetic left hand" + id = "pros_l_hand" + build_path = /obj/item/organ/external/hand + time = 15 + materials = list(DEFAULT_WALL_MATERIAL = 10000) + +/datum/design/item/prosfab/pros/l_leg + name = "Prosthetic left leg" + id = "pros_l_leg" + build_path = /obj/item/organ/external/leg + time = 20 + materials = list(DEFAULT_WALL_MATERIAL = 15000) + +/datum/design/item/prosfab/pros/l_foot + name = "Prosthetic left foot" + id = "pros_l_foot" + build_path = /obj/item/organ/external/foot + time = 15 + materials = list(DEFAULT_WALL_MATERIAL = 10000) + +/datum/design/item/prosfab/pros/r_arm + name = "Prosthetic right arm" + id = "pros_r_arm" + build_path = /obj/item/organ/external/arm/right + time = 20 + materials = list(DEFAULT_WALL_MATERIAL = 18000) + +/datum/design/item/prosfab/pros/r_hand + name = "Prosthetic right hand" + id = "pros_r_hand" + build_path = /obj/item/organ/external/hand/right + time = 15 + materials = list(DEFAULT_WALL_MATERIAL = 10000) + +/datum/design/item/prosfab/pros/r_leg + name = "Prosthetic right leg" + id = "pros_r_leg" + build_path = /obj/item/organ/external/leg/right + time = 20 + materials = list(DEFAULT_WALL_MATERIAL = 15000) + +/datum/design/item/prosfab/pros/r_foot + name = "Prosthetic right foot" + id = "pros_r_foot" + build_path = /obj/item/organ/external/foot/right + time = 15 + materials = list(DEFAULT_WALL_MATERIAL = 10000) + +/datum/design/item/prosfab/pros/cell + name = "Prosthetic powercell" + id = "pros_cell" + build_path = /obj/item/organ/internal/cell + time = 15 + materials = list(DEFAULT_WALL_MATERIAL = 10000, "glass" = 4000, "plasteel" = 2000) + req_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2) + +/datum/design/item/prosfab/pros/eyes + name = "Prosthetic eyes" + id = "pros_eyes" + build_path = /obj/item/organ/internal/eyes/robot + time = 15 + materials = list(DEFAULT_WALL_MATERIAL = 7500, "glass" = 7500) + req_tech = list(TECH_ENGINEERING = 2, TECH_MATERIAL = 2) + +//////////////////// Cyborg Parts //////////////////// +/datum/design/item/prosfab/cyborg + category = "Cyborg Parts" + time = 20 + materials = list(DEFAULT_WALL_MATERIAL = 5000) + +/datum/design/item/prosfab/cyborg/exoskeleton + name = "Robot exoskeleton" + id = "robot_exoskeleton" + build_path = /obj/item/robot_parts/robot_suit + time = 50 + materials = list(DEFAULT_WALL_MATERIAL = 50000) + +/datum/design/item/prosfab/cyborg/torso + name = "Robot torso" + id = "robot_torso" + build_path = /obj/item/robot_parts/chest + time = 35 + materials = list(DEFAULT_WALL_MATERIAL = 40000) + +/datum/design/item/prosfab/cyborg/head + name = "Robot head" + id = "robot_head" + build_path = /obj/item/robot_parts/head + time = 35 + materials = list(DEFAULT_WALL_MATERIAL = 25000) + +/datum/design/item/prosfab/cyborg/l_arm + name = "Robot left arm" + id = "robot_l_arm" + build_path = /obj/item/robot_parts/l_arm + time = 20 + materials = list(DEFAULT_WALL_MATERIAL = 18000) + +/datum/design/item/prosfab/cyborg/r_arm + name = "Robot right arm" + id = "robot_r_arm" + build_path = /obj/item/robot_parts/r_arm + time = 20 + materials = list(DEFAULT_WALL_MATERIAL = 18000) + +/datum/design/item/prosfab/cyborg/l_leg + name = "Robot left leg" + id = "robot_l_leg" + build_path = /obj/item/robot_parts/l_leg + time = 20 + materials = list(DEFAULT_WALL_MATERIAL = 15000) + +/datum/design/item/prosfab/cyborg/r_leg + name = "Robot right leg" + id = "robot_r_leg" + build_path = /obj/item/robot_parts/r_leg + time = 20 + materials = list(DEFAULT_WALL_MATERIAL = 15000) + + +//////////////////// Cyborg Internals //////////////////// +/datum/design/item/prosfab/cyborg/component + category = "Cyborg Internals" + build_type = PROSFAB + time = 12 + materials = list(DEFAULT_WALL_MATERIAL = 10000) + +/datum/design/item/prosfab/cyborg/component/binary_communication_device + name = "Binary communication device" + id = "binary_communication_device" + build_path = /obj/item/robot_parts/robot_component/binary_communication_device + +/datum/design/item/prosfab/cyborg/component/radio + name = "Radio" + id = "radio" + build_path = /obj/item/robot_parts/robot_component/radio + +/datum/design/item/prosfab/cyborg/component/actuator + name = "Actuator" + id = "actuator" + build_path = /obj/item/robot_parts/robot_component/actuator + +/datum/design/item/prosfab/cyborg/component/diagnosis_unit + name = "Diagnosis unit" + id = "diagnosis_unit" + build_path = /obj/item/robot_parts/robot_component/diagnosis_unit + +/datum/design/item/prosfab/cyborg/component/camera + name = "Camera" + id = "camera" + build_path = /obj/item/robot_parts/robot_component/camera + +/datum/design/item/prosfab/cyborg/component/armour + name = "Armour plating" + id = "armour" + build_path = /obj/item/robot_parts/robot_component/armour + + +//////////////////// Cyborg Modules //////////////////// +/datum/design/item/prosfab/robot_upgrade + category = "Cyborg Modules" + build_type = PROSFAB + time = 12 + materials = list(DEFAULT_WALL_MATERIAL = 10000) + +/datum/design/item/prosfab/robot_upgrade/rename + name = "Rename module" + desc = "Used to rename a cyborg." + id = "borg_rename_module" + build_path = /obj/item/borg/upgrade/rename + +/datum/design/item/prosfab/robot_upgrade/reset + name = "Reset module" + desc = "Used to reset a cyborg's module. Destroys any other upgrades applied to the robot." + id = "borg_reset_module" + build_path = /obj/item/borg/upgrade/reset + +/datum/design/item/prosfab/robot_upgrade/restart + name = "Emergency restart module" + desc = "Used to force a restart of a disabled-but-repaired robot, bringing it back online." + id = "borg_restart_module" + materials = list(DEFAULT_WALL_MATERIAL = 60000, "glass" = 5000) + build_path = /obj/item/borg/upgrade/restart + +/datum/design/item/prosfab/robot_upgrade/vtec + name = "VTEC module" + desc = "Used to kick in a robot's VTEC systems, increasing their speed." + id = "borg_vtec_module" + materials = list(DEFAULT_WALL_MATERIAL = 80000, "glass" = 6000, "gold" = 5000) + build_path = /obj/item/borg/upgrade/vtec + +/datum/design/item/prosfab/robot_upgrade/tasercooler + name = "Rapid taser cooling module" + desc = "Used to cool a mounted taser, increasing the potential current in it and thus its recharge rate." + id = "borg_taser_module" + materials = list(DEFAULT_WALL_MATERIAL = 80000, "glass" = 6000, "gold" = 2000, "diamond" = 500) + build_path = /obj/item/borg/upgrade/tasercooler + +/datum/design/item/prosfab/robot_upgrade/jetpack + name = "Jetpack module" + desc = "A carbon dioxide jetpack suitable for low-gravity mining operations." + id = "borg_jetpack_module" + materials = list(DEFAULT_WALL_MATERIAL = 10000, "phoron" = 15000, "uranium" = 20000) + build_path = /obj/item/borg/upgrade/jetpack + +/datum/design/item/prosfab/robot_upgrade/syndicate + name = "Illegal upgrade" + desc = "Allows for the construction of lethal upgrades for cyborgs." + id = "borg_syndicate_module" + req_tech = list(TECH_COMBAT = 4, TECH_ILLEGAL = 3) + materials = list(DEFAULT_WALL_MATERIAL = 10000, "glass" = 15000, "diamond" = 10000) + build_path = /obj/item/borg/upgrade/syndicate \ No newline at end of file diff --git a/code/modules/spells/spellbook.dm b/code/modules/spells/spellbook.dm index 822bc8da7c..b54b243609 100644 --- a/code/modules/spells/spellbook.dm +++ b/code/modules/spells/spellbook.dm @@ -42,8 +42,6 @@ This spell temporarly blinds a single person and does not require wizard garb.
Subjugation (30)
This spell temporarily subjugates a target's mind and does not require wizard garb.
- Mind Transfer (60)
- This spell allows the user to switch bodies with a target. Careful to not lose your memory in the process.
Forcewall (10)
This spell creates an unbreakable wall that lasts for 30 seconds and does not need wizard garb.
Blink (2)
@@ -56,8 +54,6 @@ This spell creates your ethereal form, temporarily making you invisible and able to pass through walls.
Knock (10)
This spell opens nearby doors and does not require wizard garb.
- Curse of the Horseman (15)
- This spell will curse a person to wear an unremovable horse mask (it has glue on the inside) and speak like a horse. It does not require wizard garb.
Remove Clothes Requirement Warning: this takes away 2 spell choices.

Artefacts:
@@ -174,10 +170,10 @@ feedback_add_details("wizard_spell_learned","SJ") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells H.add_spell(new/spell/targeted/subjugation) temp = "You have learned subjugate." - if("mindswap") - feedback_add_details("wizard_spell_learned","MT") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells - H.add_spell(new/spell/targeted/mind_transfer) - temp = "You have learned mindswap." +// if("mindswap") +// feedback_add_details("wizard_spell_learned","MT") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells +// H.add_spell(new/spell/targeted/mind_transfer) +// temp = "You have learned mindswap." if("forcewall") feedback_add_details("wizard_spell_learned","FW") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells H.add_spell(new/spell/aoe_turf/conjure/forcewall) @@ -202,10 +198,10 @@ feedback_add_details("wizard_spell_learned","KN") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells H.add_spell(new/spell/aoe_turf/knock) temp = "You have learned knock." - if("horseman") - feedback_add_details("wizard_spell_learned","HH") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells - H.add_spell(new/spell/targeted/equip_item/horsemask) - temp = "You have learned curse of the horseman." +// if("horseman") +// feedback_add_details("wizard_spell_learned","HH") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells +// H.add_spell(new/spell/targeted/equip_item/horsemask) +// temp = "You have learned curse of the horseman." if("mentalfocus") feedback_add_details("wizard_spell_learned","MF") //please do not change the abbreviation to keep data processing consistent. Add a unique id to any new spells new /obj/item/weapon/gun/energy/staff/focus(get_turf(H)) diff --git a/code/modules/surgery/bones.dm b/code/modules/surgery/bones.dm index ff0ea12635..cc4428fcb6 100644 --- a/code/modules/surgery/bones.dm +++ b/code/modules/surgery/bones.dm @@ -18,7 +18,7 @@ if (!hasorgans(target)) return 0 var/obj/item/organ/external/affected = target.get_organ(target_zone) - return affected && !(affected.status & ORGAN_ROBOT) && affected.open >= 2 && affected.stage == 0 + return affected && (affected.robotic < ORGAN_ROBOT) && affected.open >= 2 && affected.stage == 0 begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) @@ -52,7 +52,7 @@ if (!hasorgans(target)) return 0 var/obj/item/organ/external/affected = target.get_organ(target_zone) - return affected && affected.organ_tag != BP_HEAD && !(affected.status & ORGAN_ROBOT) && affected.open >= 2 && affected.stage == 1 + return affected && affected.organ_tag != BP_HEAD && !(affected.robotic >= ORGAN_ROBOT) && affected.open >= 2 && affected.stage == 1 begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) @@ -91,7 +91,7 @@ if (!hasorgans(target)) return 0 var/obj/item/organ/external/affected = target.get_organ(target_zone) - return affected && affected.organ_tag == BP_HEAD && !(affected.status & ORGAN_ROBOT) && affected.open >= 2 && affected.stage == 1 + return affected && affected.organ_tag == BP_HEAD && (affected.robotic < ORGAN_ROBOT) && affected.open >= 2 && affected.stage == 1 begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) user.visible_message("[user] is beginning to piece together [target]'s skull with \the [tool]." , \ @@ -127,7 +127,7 @@ if (!hasorgans(target)) return 0 var/obj/item/organ/external/affected = target.get_organ(target_zone) - return affected && affected.open >= 2 && !(affected.status & ORGAN_ROBOT) && affected.stage == 2 + return affected && affected.open >= 2 && !(affected.robotic >= ORGAN_ROBOT) && affected.stage == 2 begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) diff --git a/code/modules/surgery/encased.dm b/code/modules/surgery/encased.dm index 1744cec3d7..81b11d12db 100644 --- a/code/modules/surgery/encased.dm +++ b/code/modules/surgery/encased.dm @@ -12,7 +12,7 @@ return 0 var/obj/item/organ/external/affected = target.get_organ(target_zone) - return affected && !(affected.status & ORGAN_ROBOT) && affected.encased && affected.open >= 2 + return affected && !(affected.robotic >= ORGAN_ROBOT) && affected.encased && affected.open >= 2 /datum/surgery_step/open_encased/saw diff --git a/code/modules/surgery/face.dm b/code/modules/surgery/face.dm index f4e723d0c6..744b1d56dd 100644 --- a/code/modules/surgery/face.dm +++ b/code/modules/surgery/face.dm @@ -10,7 +10,7 @@ if (!hasorgans(target)) return 0 var/obj/item/organ/external/affected = target.get_organ(target_zone) - if (!affected || (affected.status & ORGAN_ROBOT)) + if (!affected || (affected.robotic >= ORGAN_ROBOT)) return 0 return target_zone == O_MOUTH diff --git a/code/modules/surgery/generic.dm b/code/modules/surgery/generic.dm index 697a5cf191..e6dc9d9860 100644 --- a/code/modules/surgery/generic.dm +++ b/code/modules/surgery/generic.dm @@ -17,7 +17,7 @@ return 0 if (affected.is_stump()) return 0 - if (affected.status & ORGAN_ROBOT) + if (affected.robotic >= ORGAN_ROBOT) return 0 return 1 diff --git a/code/modules/surgery/implant.dm b/code/modules/surgery/implant.dm index 7ea873c8d0..88df711b47 100644 --- a/code/modules/surgery/implant.dm +++ b/code/modules/surgery/implant.dm @@ -128,7 +128,7 @@ user.visible_message("\blue [user] puts \the [tool] inside [target]'s [get_cavity(affected)] cavity.", \ "\blue You put \the [tool] inside [target]'s [get_cavity(affected)] cavity." ) - if (tool.w_class > get_max_wclass(affected)/2 && prob(50) && !(affected.status & ORGAN_ROBOT)) + if (tool.w_class > get_max_wclass(affected)/2 && prob(50) && !(affected.robotic >= ORGAN_ROBOT)) user << "\red You tear some blood vessels trying to fit such a big object in this cavity." var/datum/wound/internal_bleeding/I = new (10) affected.wounds += I diff --git a/code/modules/surgery/limb_reattach.dm b/code/modules/surgery/limb_reattach.dm index eadd4db655..ee0dfecb4f 100644 --- a/code/modules/surgery/limb_reattach.dm +++ b/code/modules/surgery/limb_reattach.dm @@ -21,6 +21,18 @@ min_duration = 50 max_duration = 70 + can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) + var/obj/item/organ/external/E = tool + var/obj/item/organ/external/P = target.organs_by_name[E.parent_organ] + if(!P) + user << "There's nothing to attach [E] to!" + return 0 + else if((P.robotic >= ORGAN_ROBOT) && (E.robotic < ORGAN_ROBOT)) + user << "Attaching [E] to [P] wouldn't work well." + return 0 + else + return 1 + begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/E = tool user.visible_message("[user] starts attaching [E.name] to [target]'s [E.amputation_point].", \ diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index 9b7b006759..ccb90c20d6 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -52,7 +52,7 @@ for(var/obj/item/organ/I in affected.internal_organs) if(I && I.damage > 0) - if(!(I.status & ORGAN_ROBOT)) + if(!(I.robotic >= ORGAN_ROBOT)) user.visible_message("[user] starts treating damage to [target]'s [I.name] with [tool_name].", \ "You start treating damage to [target]'s [I.name] with [tool_name]." ) @@ -72,7 +72,7 @@ for(var/obj/item/organ/internal/I in affected.internal_organs) if(I && I.damage > 0) - if(!(I.status & ORGAN_ROBOT)) + if(!(I.robotic >= ORGAN_ROBOT)) user.visible_message("[user] treats damage to [target]'s [I.name] with [tool_name].", \ "You treat damage to [target]'s [I.name] with [tool_name]." ) I.damage = 0 @@ -119,7 +119,7 @@ var/obj/item/organ/external/affected = target.get_organ(target_zone) - if(!(affected && !(affected.status & ORGAN_ROBOT))) + if(!(affected && !(affected.robotic >= ORGAN_ROBOT))) return 0 target.op_stage.current_organ = null @@ -234,7 +234,7 @@ if(!istype(O)) return 0 - if((affected.status & ORGAN_ROBOT) && !(O.status & ORGAN_ROBOT)) + if((affected.robotic >= ORGAN_ROBOT) && !(O.robotic >= ORGAN_ROBOT)) user << "You cannot install a naked organ into a robotic body." return SURGERY_FAILURE @@ -307,7 +307,7 @@ var/list/removable_organs = list() for(var/organ in target.internal_organs_by_name) var/obj/item/organ/I = target.internal_organs_by_name[organ] - if(istype(I) && (I.status & ORGAN_CUT_AWAY) && !(I.status & ORGAN_ROBOT) && I.parent_organ == target_zone) + if(istype(I) && (I.status & ORGAN_CUT_AWAY) && !(I.robotic >= ORGAN_ROBOT) && I.parent_organ == target_zone) removable_organs |= organ var/organ_to_replace = input(user, "Which organ do you want to reattach?") as null|anything in removable_organs diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index 037eb933b2..6635753ed9 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -17,7 +17,7 @@ return 0 if (affected.status & ORGAN_DESTROYED) return 0 - if (!(affected.status & ORGAN_ROBOT)) + if (!(affected.robotic >= ORGAN_ROBOT)) return 0 return 1 @@ -213,7 +213,7 @@ if(!affected) return var/is_organ_damaged = 0 for(var/obj/item/organ/I in affected.internal_organs) - if(I.damage > 0 && (I.status & ORGAN_ROBOT)) + if(I.damage > 0 && (I.robotic >= ORGAN_ROBOT)) is_organ_damaged = 1 break return affected.open == 3 && is_organ_damaged @@ -226,7 +226,7 @@ for(var/obj/item/organ/I in affected.internal_organs) if(I && I.damage > 0) - if(I.status & ORGAN_ROBOT) + if(I.robotic >= ORGAN_ROBOT) user.visible_message("[user] starts mending the damage to [target]'s [I.name]'s mechanisms.", \ "You start mending the damage to [target]'s [I.name]'s mechanisms." ) @@ -242,7 +242,7 @@ for(var/obj/item/organ/I in affected.internal_organs) if(I && I.damage > 0) - if(I.status & ORGAN_ROBOT) + if(I.robotic >= ORGAN_ROBOT) user.visible_message("[user] repairs [target]'s [I.name] with [tool].", \ "You repair [target]'s [I.name] with [tool]." ) I.damage = 0 @@ -277,7 +277,7 @@ can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - if(!(affected && (affected.status & ORGAN_ROBOT))) + if(!(affected && (affected.robotic >= ORGAN_ROBOT))) return 0 if(affected.open < 3) return 0 @@ -326,7 +326,7 @@ can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) - if(!(affected && (affected.status & ORGAN_ROBOT))) + if(!(affected && (affected.robotic >= ORGAN_ROBOT))) return 0 if(affected.open < 3) return 0 @@ -336,7 +336,7 @@ var/list/removable_organs = list() for(var/organ in target.internal_organs_by_name) var/obj/item/organ/I = target.internal_organs_by_name[organ] - if(I && (I.status & ORGAN_CUT_AWAY) && (I.status & ORGAN_ROBOT) && I.parent_organ == target_zone) + if(I && (I.status & ORGAN_CUT_AWAY) && (I.robotic >= ORGAN_ROBOT) && I.parent_organ == target_zone) removable_organs |= organ var/organ_to_replace = input(user, "Which organ do you want to reattach?") as null|anything in removable_organs @@ -388,12 +388,11 @@ user << "That brain is not usable." return SURGERY_FAILURE - if(!(affected.status & ORGAN_ROBOT)) + if(!(affected.robotic >= ORGAN_ROBOT)) user << "You cannot install a computer brain into a meat skull." return SURGERY_FAILURE if(!target.should_have_organ("brain")) - user << "You're pretty sure [target.species.name_plural] don't normally have a brain." return SURGERY_FAILURE @@ -425,6 +424,20 @@ if(M.brainmob && M.brainmob.mind) M.brainmob.mind.transfer_to(target) + spawn(0) //Name yourself on your own damn time + var/new_name = "" + while(!new_name) + if(!target) return + var/try_name = input(target,"Pick a name for your new form!", "New Name", target.name) + var/clean_name = sanitizeName(try_name) + if(clean_name) + var/okay = alert(target,"New name will be '[clean_name]', ok?", "Cancel", "Ok") + if(okay == "Ok") + new_name = clean_name + + target.name = new_name + target.real_name = target.name + fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) user.visible_message("[user]'s hand slips.", \ "Your hand slips.") diff --git a/code/modules/xenobio2/_xeno_setup.dm b/code/modules/xenobio2/_xeno_setup.dm new file mode 100644 index 0000000000..a08cb54681 --- /dev/null +++ b/code/modules/xenobio2/_xeno_setup.dm @@ -0,0 +1,211 @@ +//Important definitions for xenos, traits datum. + +//Definitions for gene grouping. +#define GENE_XENO_BIOCHEMISTRY "biochemistry" +#define GENE_XENO_HEALTH "health" +#define GENE_XENO_RESISTANCES "resistances" +#define GENE_XENO_INTERNAL "internal" +#define GENE_XENO_COLOR "color" +#define GENE_XENO_GLOW "glow" +#define GENE_XENO_LEARN "learn" +#define GENE_XENO_SPEAK "speak" +#define GENE_XENO_SPEED "speed" +#define GENE_XENO_SPECIAL "special" + +#define ALL_XENO_GENES list(GENE_XENO_BIOCHEMISTRY, GENE_XENO_HEALTH, GENE_XENO_RESISTANCES, GENE_XENO_INTERNAL, GENE_XENO_COLOR, GENE_XENO_LEARN, GENE_XENO_SPEAK, GENE_XENO_SPEED, GENE_XENO_SPECIAL) + +//Definitions for traits. + +#define TRAIT_XENO_COLOR "color" +#define TRAIT_XENO_CHEMISTRY "chems" +#define TRAIT_XENO_CHEMVOL "chem_vol" +#define TRAIT_XENO_HEALTH "health" +#define TRAIT_XENO_HUNGER "hunger" +#define TRAIT_XENO_STARVEDAMAGE "starvedamage" +#define TRAIT_XENO_EATS "eats" +#define TRAIT_XENO_CHROMATIC "chrom" +#define TRAIT_XENO_HOSTILE "hostile" +#define TRAIT_XENO_BIOLUMESCENT "biolum" +#define TRAIT_XENO_BIO_COLOR "biocolor" +#define TRAIT_XENO_GLOW_STRENGTH "biostrength" +#define TRAIT_XENO_GLOW_RANGE "biorange" +#define TRAIT_XENO_TOXIC_CHEMS "tox_chems" +#define TRAIT_XENO_NUTRITIONAL_CHEMS "nutr_chems" +#define TRAIT_XENO_HEALING_CHEMS "heal_chems" +#define TRAIT_XENO_MUTATING_CHEMS "mut_chems" +#define TRAIT_XENO_SPEED "speed" +#define TRAIT_XENO_COLDRES "cold_resistance" +#define TRAIT_XENO_HEATRES "heat_resistance" +#define TRAIT_XENO_LEARNCHANCE "learn_chance" +#define TRAIT_XENO_CANLEARN "can_learn" +#define TRAIT_XENO_SPEAKCHANCE "speak_chance" +#define TRAIT_XENO_CANSPEAK "can_speak" +#define TRAIT_XENO_STRENGTH "melee_strength" +#define TRAIT_XENO_STR_RANGE "melee_range" + +//Maleability defines. +#define MAX_MALEABLE 2 +#define MINOR_MALEABLE 1 +#define MIN_MALEABLE 0 + +var/global/list/xenoChemList = list("mutationtoxin", + "psilocybin", + "mindbreaker", + "impedrezene", + "cryptobiolin", + "space_drugs", + "chloralhydrate", + "stoxin", + "mutagen", + "lexorin", + "pacid", + "cyanide", + "phoron", + "plasticide", + "amatoxin", + "carbon", + "radium", + "sacid", + "sugar", + "kelotane", + "dermaline", + "anti_toxin", + "dexalin", + "synaptizine", + "alkysine", + "imidazoline", + "peridaxon", + "rezadone", + "mutationtoxin", + "docilitytoxin") + +/datum/xeno/traits + var/list/traits = list() + var/list/chemlist = list() + var/list/chems = list() + var/source = "unknown" + +/datum/xeno/traits/proc/set_trait(var/trait, var/newval) + traits["[trait]"] = newval + return + +/datum/xeno/traits/proc/get_trait(var/trait) + var/val = traits["[trait]"] + return val + +/datum/xeno/traits/New() + ..() + set_trait(TRAIT_XENO_COLOR, "#CACACA") + set_trait(TRAIT_XENO_CHEMVOL, 100) + set_trait(TRAIT_XENO_HEALTH, 20) + set_trait(TRAIT_XENO_HUNGER, 2) + set_trait(TRAIT_XENO_STARVEDAMAGE, 0) + set_trait(TRAIT_XENO_EATS, 1) + set_trait(TRAIT_XENO_CHROMATIC, 0) + set_trait(TRAIT_XENO_HOSTILE, 0) + set_trait(TRAIT_XENO_BIOLUMESCENT, 0) + set_trait(TRAIT_XENO_BIO_COLOR, "#CACACA") + set_trait(TRAIT_XENO_GLOW_STRENGTH, 0) + set_trait(TRAIT_XENO_GLOW_RANGE, 0) + set_trait(TRAIT_XENO_SPEED, 4) + set_trait(TRAIT_XENO_COLDRES, 20) + set_trait(TRAIT_XENO_HEATRES, 20) + set_trait(TRAIT_XENO_LEARNCHANCE, 100) + set_trait(TRAIT_XENO_CANLEARN, 1) + set_trait(TRAIT_XENO_SPEAKCHANCE, 1) + set_trait(TRAIT_XENO_CANSPEAK, 1) + set_trait(TRAIT_XENO_STRENGTH, 0) + set_trait(TRAIT_XENO_STR_RANGE, 0) + +/datum/xeno/traits/proc/get_gene(var/genetype) + + if(!genetype) return 0 + + var/datum/xeno/genes/G = new() + G.genetype = genetype + G.values = list() + + switch(genetype) + if(GENE_XENO_BIOCHEMISTRY) + G.values["[TRAIT_XENO_CHEMVOL]"] = get_trait(TRAIT_XENO_CHEMVOL) + G.chems = chems + if(GENE_XENO_HEALTH) + G.values["[TRAIT_XENO_HEALTH]"] = get_trait(TRAIT_XENO_HEALTH) + G.values["[TRAIT_XENO_STRENGTH]"] = get_trait(TRAIT_XENO_STRENGTH) + G.values["[TRAIT_XENO_STR_RANGE]"] = get_trait(TRAIT_XENO_STR_RANGE) + if(GENE_XENO_RESISTANCES) + G.values["[TRAIT_XENO_COLDRES]"] = get_trait(TRAIT_XENO_HEALTH) + G.values["[TRAIT_XENO_HEATRES]"] = get_trait(TRAIT_XENO_STRENGTH) + if(GENE_XENO_INTERNAL) + G.values["[TRAIT_XENO_NUTRITIONAL_CHEMS]"] = get_trait(TRAIT_XENO_NUTRITIONAL_CHEMS) + G.values["[TRAIT_XENO_HEALING_CHEMS]"] = get_trait(TRAIT_XENO_HEALING_CHEMS) + G.values["[TRAIT_XENO_TOXIC_CHEMS]"] = get_trait(TRAIT_XENO_TOXIC_CHEMS) + G.values["[TRAIT_XENO_MUTATING_CHEMS]"] = get_trait(TRAIT_XENO_MUTATING_CHEMS) + if(GENE_XENO_COLOR) + G.values["[TRAIT_XENO_COLOR]"] = get_trait(TRAIT_XENO_COLOR) + G.values["[TRAIT_XENO_BIO_COLOR]"] = get_trait(TRAIT_XENO_BIO_COLOR) + if(GENE_XENO_GLOW) + G.values["[TRAIT_XENO_BIOLUMESCENT]"] = get_trait(TRAIT_XENO_BIOLUMESCENT) + G.values["[TRAIT_XENO_GLOW_RANGE]"] = get_trait(TRAIT_XENO_GLOW_RANGE) + G.values["[TRAIT_XENO_GLOW_STRENGTH]"] = get_trait(TRAIT_XENO_GLOW_STRENGTH) + if(GENE_XENO_LEARN) + G.values["[TRAIT_XENO_LEARNCHANCE]"] = get_trait(TRAIT_XENO_LEARNCHANCE) + G.values["[TRAIT_XENO_CANLEARN]"] = get_trait(TRAIT_XENO_CANLEARN) + if(GENE_XENO_SPEAK) + G.values["[TRAIT_XENO_SPEAKCHANCE]"] = get_trait(TRAIT_XENO_SPEAKCHANCE) + G.values["[TRAIT_XENO_CANSPEAK]"] = get_trait(TRAIT_XENO_CANSPEAK) + if(GENE_XENO_SPEED) + G.values["[TRAIT_XENO_SPEED]"] = get_trait(TRAIT_XENO_SPEED) + if(GENE_XENO_SPECIAL) + G.values["[TRAIT_XENO_HOSTILE]"] = get_trait(TRAIT_XENO_HOSTILE) + G.values["[TRAIT_XENO_CHROMATIC]"] = get_trait(TRAIT_XENO_CHROMATIC) + + return G + +/datum/xeno/traits/proc/apply_gene(var/datum/xeno/genes/genes) + if(!genes.genetype) return 0 + + switch(genes.genetype) + if(GENE_XENO_BIOCHEMISTRY) + set_trait(TRAIT_XENO_CHEMVOL, genes.values["[TRAIT_XENO_CHEMVOL]"]) + for(var/reagent in genes.chems) + chems[reagent] = genes[reagent] + + if(GENE_XENO_HEALTH) + set_trait(TRAIT_XENO_HEALTH, genes.values["[TRAIT_XENO_HEALTH]"]) + set_trait(TRAIT_XENO_STRENGTH, genes.values["[TRAIT_XENO_STRENGTH]"]) + set_trait(TRAIT_XENO_STR_RANGE, genes.values["[TRAIT_XENO_STR_RANGE]"]) + if(GENE_XENO_RESISTANCES) + set_trait(TRAIT_XENO_COLDRES, genes.values["[TRAIT_XENO_COLDRES]"]) + set_trait(TRAIT_XENO_HEATRES, genes.values["[TRAIT_XENO_HEATRES]"]) + if(GENE_XENO_INTERNAL) + set_trait(TRAIT_XENO_NUTRITIONAL_CHEMS, genes.values["[TRAIT_XENO_NUTRITIONAL_CHEMS]"]) + set_trait(TRAIT_XENO_HEALING_CHEMS, genes.values["[TRAIT_XENO_HEALING_CHEMS]"]) + set_trait(TRAIT_XENO_TOXIC_CHEMS, genes.values["[TRAIT_XENO_TOXIC_CHEMS]"]) + set_trait(TRAIT_XENO_MUTATING_CHEMS, genes.values["[TRAIT_XENO_MUTATING_CHEMS]"]) + if(GENE_XENO_COLOR) + set_trait(TRAIT_XENO_COLOR, genes.values["[TRAIT_XENO_COLOR]"]) + set_trait(TRAIT_XENO_BIO_COLOR, genes.values["[TRAIT_XENO_BIO_COLOR]"]) + if(GENE_XENO_GLOW) + set_trait(TRAIT_XENO_BIOLUMESCENT, genes.values["[TRAIT_XENO_BIOLUMESCENT]"]) + set_trait(TRAIT_XENO_GLOW_RANGE, genes.values["[TRAIT_XENO_GLOW_RANGE]"]) + set_trait(TRAIT_XENO_GLOW_STRENGTH, genes.values["[TRAIT_XENO_GLOW_STRENGTH]"]) + if(GENE_XENO_LEARN) + set_trait(TRAIT_XENO_LEARNCHANCE, genes.values["[TRAIT_XENO_LEARNCHANCE]"]) + set_trait(TRAIT_XENO_CANLEARN, genes.values["[TRAIT_XENO_CANLEARN]"]) + if(GENE_XENO_SPEAK) + set_trait(TRAIT_XENO_SPEAKCHANCE, genes.values["[TRAIT_XENO_SPEAKCHANCE]"]) + set_trait(TRAIT_XENO_CANSPEAK, genes.values["[TRAIT_XENO_CANSPEAK]"]) + if(GENE_XENO_SPEED) + set_trait(TRAIT_XENO_SPEED ,genes.values["[TRAIT_XENO_SPEED]"]) + if(GENE_XENO_SPECIAL) + set_trait(TRAIT_XENO_HOSTILE, genes.values["[TRAIT_XENO_HOSTILE]"]) + set_trait(TRAIT_XENO_CHROMATIC, genes.values["[TRAIT_XENO_CHROMATIC]"]) + +/datum/xeno/genes + var/genetype //Label for specifying what gene is used. + var/list/values //What's going to be put into specific traits + var/list/chems + + + \ No newline at end of file diff --git a/code/modules/xenobio2/controller.dm b/code/modules/xenobio2/controller.dm new file mode 100644 index 0000000000..112d667c0f --- /dev/null +++ b/code/modules/xenobio2/controller.dm @@ -0,0 +1,44 @@ +//Used to create the gene mask. Shamelessly stolen from the plant controller and cut down. + +/client/proc/show_xenobio_genes() + set category = "Debug" + set name = "Show Xenobio Genes" + set desc = "Prints the round's plant xenobio masks." + + if(!holder) return + + if(!xenobio_controller || !xenobio_controller.gene_tag_masks) + usr << "Gene masks not set." + return + + for(var/mask in xenobio_controller.gene_tag_masks) + usr << "[mask]: [xenobio_controller.gene_tag_masks[mask]]" + +var/global/datum/controller/xenobio/xenobio_controller // Set in New(). + +/datum/controller/xenobio + + var/list/gene_tag_masks = list() // Gene obfuscation for delicious trial and error goodness. + +/datum/controller/xenobio/New() + if(xenobio_controller && xenobio_controller != src) + log_debug("Rebuilding xenobio controller.") + qdel(xenobio_controller) + xenobio_controller = src + setup() + + +/datum/controller/xenobio/proc/setup() + + var/list/used_masks = list() + var/list/xenobio_traits = ALL_XENO_GENES + while(xenobio_traits && xenobio_traits.len) + var/gene_tag = pick(xenobio_traits) + var/gene_mask = "[uppertext(num2hex(rand(0,255)))]" + + while(gene_mask in used_masks) + gene_mask = "[uppertext(num2hex(rand(0,255)))]" + + used_masks += gene_mask + xenobio_traits -= gene_tag + gene_tag_masks[gene_tag] = gene_mask \ No newline at end of file diff --git a/code/modules/xenobio2/machinery/core_extractor.dm b/code/modules/xenobio2/machinery/core_extractor.dm new file mode 100644 index 0000000000..421c8ed22f --- /dev/null +++ b/code/modules/xenobio2/machinery/core_extractor.dm @@ -0,0 +1,193 @@ +/* + Here lives the slime core extractor + This machine extracts slime cores at the cost of the slime itself. + To create more of these slimes, stick the slime core in the extractor. +*/ +/obj/machinery/slime/extractor + name = "Slime extractor" + desc = "A machine for cutting up slimes to get to their cores." + icon = 'icons/obj/xenoarchaeology.dmi' + icon_state = "scanner_0old" + density = 1 + anchored = 1 + circuit = /obj/item/weapon/circuitboard/slimeextractor + var/inuse + var/mob/living/simple_animal/xeno/slime/occupant = null + var/occupiedcolor = "#22FF22" + var/emptycolor = "#FF2222" + var/operatingcolor = "#FFFF22" + + +/obj/machinery/slime/extractor/New() + ..() + update_light_color() + circuit = new circuit(src) + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/micro_laser(src) + component_parts += new /obj/item/weapon/stock_parts/micro_laser(src) + RefreshParts() + +/obj/machinery/slime/extractor/attackby(var/obj/item/W, var/mob/user) + + //Let's try to deconstruct first. + if(istype(W, /obj/item/weapon/screwdriver) && !inuse) + default_deconstruction_screwdriver(user, W) + return + + if(istype(W, /obj/item/weapon/crowbar)) + default_deconstruction_crowbar(user, W) + return + + if(panel_open) + user << "Close the panel first!" + + var/obj/item/weapon/grab/G = W + + if(!istype(G)) + return ..() + + if(G.state < 2) + user << "You need a better grip to do that!" + return + + move_into_extractor(user,G.affecting) + +/obj/machinery/slime/extractor/MouseDrop_T(mob/target, mob/user) + if(user.stat || user.restrained()) + return + move_into_extractor(user,target) + +/obj/machinery/slime/extractor/proc/move_into_extractor(var/mob/user,var/mob/living/victim) + + if(src.occupant) + user << "The core extractor is full, empty it first!" + return + + if(inuse) + user << "The core extractor is locked and running, wait for it to finish." + return + + if(!(istype(victim, /mob/living/simple_animal/xeno/slime)) ) + user << "This is not a suitable subject for the core extractor!" + return + + var/mob/living/simple_animal/xeno/slime/S = victim + if(S.is_child) + user << "This subject is not developed enough for the core extractor!" + return + + user.visible_message("[user] starts to put [victim] into the core extractor!") + src.add_fingerprint(user) + if(do_after(user, 30) && victim.Adjacent(src) && user.Adjacent(src) && victim.Adjacent(user) && !occupant) + user.visible_message("[user] stuffs [victim] into the core extractor!") + if(victim.client) + victim.client.perspective = EYE_PERSPECTIVE + victim.client.eye = src + victim.forceMove(src) + src.occupant = victim + update_light_color() + +/obj/machinery/slime/extractor/proc/update_light_color() + if(src.occupant && !(inuse)) + set_light(2, 2, occupiedcolor) + else if(src.occupant) + set_light(2, 2, operatingcolor) + else + set_light(2, 2, emptycolor) + +/obj/machinery/slime/extractor/proc/extract_cores() + if(!src.occupant) + src.visible_message("\icon[src] [src] pings unhappily.") + else if(inuse) + return + + inuse = 1 + update_light_color() + spawn(30) + icon_state = "scanner_1old" + for(var/i=1 to occupant.cores) + var/obj/item/xenoproduct/slime/core/C = new(src) + C.traits = occupant.traitdat + + C.nameVar = occupant.nameVar + + C.create_reagents(C.traits.traits[TRAIT_XENO_CHEMVOL]) + for(var/reagent in occupant.traitdat.chems) + C.reagents.add_reagent(reagent, occupant.traitdat.chems[reagent]) + + C.color = C.traits.traits[TRAIT_XENO_COLOR] + if(occupant.traitdat.get_trait(TRAIT_XENO_BIOLUMESCENT)) + C.set_light(occupant.traitdat.get_trait(TRAIT_XENO_GLOW_STRENGTH),occupant.traitdat.get_trait(TRAIT_XENO_GLOW_RANGE), occupant.traitdat.get_trait(TRAIT_XENO_BIO_COLOR)) + + spawn(30) + icon_state = "scanner_0old" + qdel(occupant) + inuse = 0 + eject_contents() + update_light_color() + src.updateUsrDialog() + +/obj/machinery/slime/extractor/proc/eject_slime() + if(occupant) + occupant.forceMove(loc) + occupant = null + +/obj/machinery/slime/extractor/proc/eject_core() + for(var/obj/thing in (contents - component_parts - circuit)) + thing.forceMove(loc) + +/obj/machinery/slime/extractor/proc/eject_contents() + eject_core() + eject_slime() + +//Here lies the UI +/obj/machinery/slime/extractor/attack_hand(mob/user as mob) + user.set_machine(src) + interact(user) + +/obj/machinery/slime/extractor/interact(mob/user as mob) + var/dat = "" + if(!inuse) + dat = {" + Slime held:
+ [occupant]
+ "} + if (occupant && !(stat & (NOPOWER|BROKEN))) + dat += "Start the core extraction.
" + if(occupant) + dat += "Eject the slime
" + else + dat += "Please wait..." + var/datum/browser/popup = new(user, "Slime Extractor", "Slime Extractor", src) + popup.set_content(dat) + popup.open() + return + + +/obj/machinery/slime/extractor/Topic(href, href_list) + if(..()) + return + usr.set_machine(src) + switch(href_list["action"]) + if ("extract") + extract_cores() + if("eject") + eject_slime() + src.updateUsrDialog() + return + +//Circuit board below, +/obj/item/weapon/circuitboard/slimeextractor + name = T_BOARD("Slime extractor") + build_path = "/obj/machinery/slime/extractor" + board_type = "machine" + origin_tech = list(TECH_DATA = 3, TECH_BIO = 3) + req_components = list( + /obj/item/weapon/stock_parts/manipulator = 2, + /obj/item/weapon/stock_parts/matter_bin = 1, + /obj/item/weapon/stock_parts/micro_laser = 2 + ) + \ No newline at end of file diff --git a/code/modules/xenobio2/machinery/gene_manipulators.dm b/code/modules/xenobio2/machinery/gene_manipulators.dm new file mode 100644 index 0000000000..f5d124a9f1 --- /dev/null +++ b/code/modules/xenobio2/machinery/gene_manipulators.dm @@ -0,0 +1,429 @@ +/* + This file contains: + + Xenobiological disk: + Holds traits that can be taken from cores and transplanted into slimes. + + Biological Product Destructive Analyzer: + Takes certain traits in gene grouping from a core and places them into a disk. + + Biological genetic bombarder: + Takes traits from a disk and replaces/adds to the genes in a xenobiological creature. +*/ +/obj/item/weapon/disk/xenobio + name = "biological data disk" + desc = "A small disk used for carrying data on genetics." + icon = 'icons/obj/hydroponics_machines.dmi' + icon_state = "disk" + w_class = 1.0 + + var/list/genes = list() + var/genesource = "unknown" + +/obj/item/weapon/disk/xenobio/attack_self(var/mob/user as mob) + if(genes.len) + var/choice = alert(user, "Are you sure you want to wipe the disk?", "Xenobiological Data", "No", "Yes") + if(src && user && genes && choice && choice == "Yes" && user.Adjacent(get_turf(src))) + user << "You wipe the disk data." + name = initial(name) + desc = initial(name) + genes = list() + genesource = "unknown" + +/obj/item/weapon/storage/box/xenobiodisk + name = "biological disk box" + desc = "A box of biological data disks, apparently." + +/obj/item/weapon/storage/box/xenobiodisk/New() + ..() + for(var/i = 0 to 7) + new /obj/item/weapon/disk/xenobio(src) + +/obj/machinery/xenobio + density = 1 + anchored = 1 + use_power = 1 + + var/obj/item/weapon/disk/xenobio/loaded_disk //Currently loaded data disk. + + var/open = 0 + var/active = 0 + var/action_time = 5 + var/last_action = 0 + var/eject_disk = 0 + var/failed_task = 0 + var/disk_needs_genes = 0 + +/obj/machinery/xenobio/attack_ai(mob/user as mob) + return attack_hand(user) + +/obj/machinery/xenobio/attack_hand(mob/user as mob) + ui_interact(user) + +/obj/machinery/xenobio/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(default_deconstruction_screwdriver(user, W)) + return + if(default_deconstruction_crowbar(user, W)) + return + if(istype(W,/obj/item/weapon/disk/xenobio)) + if(loaded_disk) + user << "There is already a data disk loaded." + return + else + var/obj/item/weapon/disk/xenobio/B = W + + if(B.genes && B.genes.len) + if(!disk_needs_genes) + user << "That disk already has gene data loaded." + return + else + if(disk_needs_genes) + user << "That disk does not have any gene data loaded." + return + + user.drop_from_inventory(W) + W.forceMove(src) + loaded_disk = W + user << "You load [W] into [src]." + + return + ..() + +/obj/machinery/xenobio/process() + + ..() + if(!active) return + + if(world.time > last_action + action_time) + finished_task() + +/obj/machinery/xenobio/proc/finished_task() + active = 0 + if(failed_task) + failed_task = 0 + visible_message("\icon[src] [src] pings unhappily, flashing a red warning light.") + else + visible_message("\icon[src] [src] pings happily.") + + if(eject_disk) + eject_disk = 0 + if(loaded_disk) + loaded_disk.forceMove(get_turf(src)) + visible_message("\icon[src] [src] beeps and spits out [loaded_disk].") + loaded_disk = null + +/obj/machinery/xenobio/extractor + name = "biological product destructive analyzer" + icon = 'icons/obj/hydroponics_machines.dmi' + icon_state = "traitcopier" + circuit = /obj/item/weapon/circuitboard/bioproddestanalyzer + + var/obj/item/xenoproduct/product + var/datum/xeno/traits/genetics // Currently scanned xeno genetic structure. + var/degradation = 0 // Increments with each scan, stops allowing gene mods after a certain point. + +/obj/machinery/xenobio/extractor/New() + ..() + circuit = new circuit(src) + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/scanning_module(src) + component_parts += new /obj/item/weapon/stock_parts/scanning_module(src) + component_parts += new /obj/item/weapon/stock_parts/scanning_module(src) + RefreshParts() + +/obj/machinery/xenobio/extractor/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W,/obj/item/xenoproduct)) + if(product) + user << "There is already a xenobiological product loaded." + return + else + var/obj/item/xenoproduct/B = W + user.drop_from_inventory(B) + B.forceMove(src) + product = B + user << "You load [B] into [src]." + + return + ..() + +/obj/machinery/xenobio/extractor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(!user) + return + + var/list/data = list() + + var/list/geneMasks[0] + for(var/gene_tag in xenobio_controller.gene_tag_masks) + geneMasks.Add(list(list("tag" = gene_tag, "mask" = xenobio_controller.gene_tag_masks[gene_tag]))) + data["geneMasks"] = geneMasks + + data["activity"] = active + data["degradation"] = degradation + + if(loaded_disk) + data["disk"] = 1 + else + data["disk"] = 0 + + if(product) + data["loaded"] = "[product.source] [product.product]" + else + data["loaded"] = 0 + + if(genetics) + data["hasGenetics"] = 1 + data["sourceName"] = genetics.source + else + data["hasGenetics"] = 0 + data["sourceName"] = 0 + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "xenobio_isolator.tmpl", "B.P.D. Analyzer UI", 470, 450) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/xenobio/proc/eject_disk() + if(!loaded_disk) return + loaded_disk.forceMove(loc) + visible_message("\icon[src] [src] beeps and spits out [loaded_disk].") + loaded_disk = null + +/obj/machinery/xenobio/extractor/Topic(href, href_list) + + if(..()) + return 1 + + if(href_list["eject_product"]) + if(!product) return + + product.forceMove(get_turf(src)) + visible_message("\icon[src] [src] beeps and spits out [product].") + product = null + + if(href_list["eject_disk"]) + eject_disk() + + if(href_list["scan_genome"]) + + if(!product) return + + last_action = world.time + active = 1 + + if(product && product.product) + genetics = product.traits + degradation = 0 + + qdel(product) + product = null + + if(href_list["get_gene"]) + + if(!genetics || !loaded_disk) return + + last_action = world.time + active = 1 + + var/datum/xeno/genes/G = genetics.get_gene(href_list["get_gene"]) + if(!G) return + loaded_disk.genes += G + + loaded_disk.genesource = "[genetics.source]" + + loaded_disk.name += " ([xenobio_controller.gene_tag_masks[href_list["get_gene"]]], [genetics.source])" + loaded_disk.desc += " The label reads \'gene [xenobio_controller.gene_tag_masks[href_list["get_gene"]]], sampled from [genetics.source]\'." + eject_disk = 1 + + degradation += rand(20,60) + if(degradation >= 100) + failed_task = 1 + genetics = null + degradation = 0 + + if(href_list["clear_buffer"]) + if(!genetics) return + genetics = null + degradation = 0 + + usr.set_machine(src) + src.add_fingerprint(usr) + + src.updateUsrDialog() + return + +/obj/machinery/xenobio/editor + name = "biological genetic bombarder" + icon = 'icons/obj/cryogenics.dmi' + icon_state = "cellold0" + disk_needs_genes = 1 + circuit = /obj/item/weapon/circuitboard/biobombarder + + var/mob/living/simple_animal/xeno/slime/occupant + +/obj/machinery/xenobio/editor/New() + ..() + circuit = new circuit(src) + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/scanning_module(src) + component_parts += new /obj/item/weapon/stock_parts/scanning_module(src) + RefreshParts() + +/obj/machinery/xenobio/editor/attackby(obj/item/weapon/W as obj, mob/user as mob) + if(istype(W,/obj/item/weapon/grab)) + var/obj/item/weapon/grab/G = W + if(occupant) + user << "There is already an organism loaded." + return + else + if(isxeno(G.affecting)) + var/mob/living/simple_animal/xeno/X = G.affecting + if(do_after(user, 30) && X.Adjacent(src) && user.Adjacent(src) && X.Adjacent(user) && !occupant) + user.drop_from_inventory(G) + X.forceMove(src) + occupant = X + user << "You load [X] into [src]." + else + user << "This specimen is incompatible with the machinery!" + return + ..() + + +/obj/machinery/xenobio/editor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + + if(!user) + return + + var/list/data = list() + + data["activity"] = active + + if(occupant) + data["degradation"] = occupant.stability + else + data["degradation"] = 0 + + if(loaded_disk && loaded_disk.genes.len) + data["disk"] = 1 + data["sourceName"] = loaded_disk.genesource + data["locus"] = "" + + for(var/datum/xeno/genes/X in loaded_disk.genes) + if(data["locus"] != "") data["locus"] += ", " + data["locus"] += "[xenobio_controller.gene_tag_masks[X.genetype]]" + + else + data["disk"] = 0 + data["sourceName"] = 0 + data["locus"] = 0 + + if(occupant) + data["loaded"] = "[occupant]" + else + data["loaded"] = 0 + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "xenobio_editor.tmpl", "Biological Genetic Bombarder UI", 470, 450) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/xenobio/editor/Topic(href, href_list) + + if(..()) + return 1 + + if(href_list["apply_gene"]) + if(!loaded_disk || !occupant) return + + last_action = world.time + active = 1 + occupant.nameVar = "modified" + + if(prob(occupant.stability)) + failed_task = 1 + occupant.stability = 101 + + for(var/datum/xeno/genes/gene in loaded_disk.genes) + occupant.traitdat.apply_gene(gene) + occupant.stability += rand(5,10) + occupant.ProcessTraits() + + if(href_list["eject_disk"]) + eject_disk() + + if(href_list["eject_xeno"]) + eject_xeno() + + usr.set_machine(src) + src.add_fingerprint(usr) + +/obj/machinery/xenobio/editor/MouseDrop_T(mob/target, mob/user) + if(user.stat || user.restrained()) + return + move_into_editor(user,target) + +/obj/machinery/xenobio/editor/proc/move_into_editor(var/mob/user,var/mob/living/victim) + + if(src.occupant) + user << "The biological genetic bombarder is full, empty it first!" + return + + if(in_use) + user << "The biological genetic bombarder is locked and running, wait for it to finish." + return + + if(!(istype(victim, /mob/living/simple_animal/xeno/slime)) ) + user << "This is not a suitable subject for the biological genetic bombarder!" + return + + user.visible_message("[user] starts to put [victim] into the biological genetic bombarder!") + src.add_fingerprint(user) + if(do_after(user, 30) && victim.Adjacent(src) && user.Adjacent(src) && victim.Adjacent(user) && !occupant) + user.visible_message("[user] stuffs [victim] into the biological genetic bombarder!") + if(victim.client) + victim.client.perspective = EYE_PERSPECTIVE + victim.client.eye = src + victim.forceMove(src) + occupant = victim + +/obj/machinery/xenobio/editor/proc/eject_contents() + eject_disk() + eject_xeno() + +/obj/machinery/xenobio/editor/proc/eject_xeno() + if(occupant) + occupant.forceMove(loc) + occupant = null + +/obj/item/weapon/circuitboard/bioproddestanalyzer + name = T_BOARD("biological product destructive analyzer") + build_path = "/obj/machinery/xenobio/extractor" + board_type = "machine" + origin_tech = list(TECH_DATA = 4, TECH_BIO = 4) + req_components = list( + /obj/item/weapon/stock_parts/manipulator = 2, + /obj/item/weapon/stock_parts/matter_bin = 1, + /obj/item/weapon/stock_parts/scanning_module = 3 + ) + +/obj/item/weapon/circuitboard/biobombarder + name = T_BOARD("biological genetic bombarder") + build_path = "/obj/machinery/xenobio/editor" + board_type = "machine" + origin_tech = list(TECH_DATA = 4, TECH_BIO = 4) + req_components = list( + /obj/item/weapon/stock_parts/manipulator = 2, + /obj/item/weapon/stock_parts/matter_bin = 2, + /obj/item/weapon/stock_parts/scanning_module = 2 + ) + \ No newline at end of file diff --git a/code/modules/xenobio2/machinery/injector.dm b/code/modules/xenobio2/machinery/injector.dm new file mode 100644 index 0000000000..a3f88825c5 --- /dev/null +++ b/code/modules/xenobio2/machinery/injector.dm @@ -0,0 +1,145 @@ +/* + This file contains: + + Manual Injector: + Manually injects chemicals into a xenobiological creature from a linked machine. + +*/ +/obj/machinery/xenobio2/manualinjector + name = "biological injector" + desc = "Injects biological organisms that are inserted with the contents of an inserted beaker at the command of a remote computer." + density = 1 + anchored = 1 + use_power = 1 + icon = 'icons/obj/biogenerator.dmi' + icon_state = "biogen-work" + var/mob/living/occupant + var/obj/item/weapon/reagent_containers/glass/beaker + var/obj/machinery/computer/xenobio2/computer + + circuit = /obj/item/weapon/circuitboard/xenobioinjectormachine + +/obj/machinery/xenobio2/manualinjector/New() + ..() + var/datum/reagents/R = new/datum/reagents(1000) + reagents = R + R.my_atom = src + beaker = new /obj/item/weapon/reagent_containers/glass/beaker(src) + circuit = new circuit(src) + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + RefreshParts() + +/obj/machinery/xenobio2/manualinjector/update_icon() + if(beaker) + if(occupant) + icon_state = "biogen-stand" + else + icon_state = "biogen-work" + else + icon_state = "biogen-empty" + +/obj/machinery/xenobio2/manualinjector/MouseDrop_T(mob/target, mob/user) + if(user.stat || user.restrained()) + return + move_into_injector(user,target) + +/obj/machinery/xenobio2/manualinjector/proc/move_into_injector(var/mob/user,var/mob/living/victim) + if(src.occupant) + user << "The injector is full, empty it first!" + return + + if(!(istype(victim, /mob/living/simple_animal/xeno)) && !emagged) + user << "This is not a suitable subject for the injector!" + return + + user.visible_message("[user] starts to put [victim] into the injector!") + src.add_fingerprint(user) + if(do_after(user, 30) && victim.Adjacent(src) && user.Adjacent(src) && victim.Adjacent(user) && !occupant) + user.visible_message("[user] stuffs [victim] into the injector!") + if(victim.client) + victim.client.perspective = EYE_PERSPECTIVE + victim.client.eye = src + victim.forceMove(src) + src.occupant = victim + +/obj/machinery/xenobio2/manualinjector/proc/eject_contents() + for(var/obj/thing in (contents - component_parts - circuit - beaker)) + thing.forceMove(loc) + if(occupant) + occupant.forceMove(loc) + occupant = null + return + +/obj/machinery/xenobio2/manualinjector/proc/eject_beaker() + if(beaker) + var/obj/item/weapon/reagent_containers/glass/beaker/B = beaker + B.loc = loc + beaker = null + return + +/obj/machinery/xenobio2/manualinjector/proc/inject_reagents() + if(!occupant) + return + if(isxeno(occupant)) + var/mob/living/simple_animal/xeno/X = occupant + beaker.reagents.trans_to_holder(X.reagents, computer.transfer_amount, 1, 0) + else + beaker.reagents.trans_to_mob(occupant, computer.transfer_amount) + +/obj/machinery/xenobio2/manualinjector/attackby(var/obj/item/W, var/mob/user) + + //Let's try to deconstruct first. + if(istype(W, /obj/item/weapon/screwdriver)) + default_deconstruction_screwdriver(user, W) + return + + if(istype(W, /obj/item/weapon/crowbar) && !occupant) + default_deconstruction_crowbar(user, W) + return + + //are you smashing a beaker in me? Well then insert that shit! + if(istype(W, /obj/item/weapon/reagent_containers/glass/beaker)) + beaker = W + user.drop_from_inventory(W) + W.loc = src + return + + //Did you want to link it? + if(istype(W, /obj/item/device/multitool)) + var/obj/item/device/multitool/P = W + if(P.connectable) + if(istype(P.connectable, /obj/machinery/computer/xenobio2)) + var/obj/machinery/computer/xenobio2/C = P.connectable + computer = C + C.injector = src + user << " You link the [src] to the [P.connectable]!" + else + user << " You store the [src] in the [P]'s buffer!" + P.connectable = src + return + + if(panel_open) + user << "Close the panel first!" + + var/obj/item/weapon/grab/G = W + + if(!istype(G)) + return ..() + + if(G.state < 2) + user << "You need a better grip to do that!" + return + + move_into_injector(user,G.affecting) + + +/obj/item/weapon/circuitboard/xenobioinjectormachine + name = T_BOARD("biological injector") + build_path = "/obj/machinery/xenobio2/manualinjector" + board_type = "machine" + origin_tech = list() //To be filled, + req_components = list() //To be filled, \ No newline at end of file diff --git a/code/modules/xenobio2/machinery/injector_computer.dm b/code/modules/xenobio2/machinery/injector_computer.dm new file mode 100644 index 0000000000..f65599e485 --- /dev/null +++ b/code/modules/xenobio2/machinery/injector_computer.dm @@ -0,0 +1,114 @@ +/* + This file contains: + + Manual Injector: + Manually injects chemicals into a xenobiological creature from a linked machine. + +*/ +/obj/machinery/computer/xenobio2 + name = "injector control console" + desc = "Used to control mechanized biological injectors." + icon_keyboard = "med_key" + icon_screen = "dna" + light_color = "#315ab4" + use_power = 1 + idle_power_usage = 250 + active_power_usage = 500 + circuit = /obj/item/weapon/circuitboard/xenobio2computer + var/obj/machinery/xenobio2/manualinjector/injector + var/transfer_amount + var/active + +/obj/machinery/computer/xenobio2/Destroy() + ..() + injector.computer = null + +/obj/machinery/computer/xenobio2/attack_hand(mob/user) + if(..()) + return 1 + ui_interact(user) + +/obj/machinery/computer/xenobio2/attackby(var/obj/item/W, var/mob/user) + + //Did you want to link it? + if(istype(W, /obj/item/device/multitool)) + var/obj/item/device/multitool/P = W + if(P.connectable) + if(istype(P.connectable, /obj/machinery/xenobio2/manualinjector)) + var/obj/machinery/xenobio2/manualinjector/I = P.connectable + injector = I + I.computer = src + user << " You link the [src] to the [P.connectable]!" + else + user << " You store the [src] in the [P]'s buffer!" + P.connectable = src + return + + ..() + +/obj/machinery/computer/xenobio2/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(!user) + return + if(!injector) + return + + var/list/data = list() + + data["activity"] = active + data["beaker"] = injector.beaker + if(injector.occupant) + data["occupied"] = 1 + if(isxeno(injector.occupant)) + var/mob/living/simple_animal/xeno/X = injector.occupant + data["compatible"] = 1 + data["instability"] = 100 * (X.mut_level / X.mut_max) + else + data["compatible"] = null + + if(injector.beaker) + data["reagentAmount"] = injector.beaker.reagents.total_volume + data["reagentMax"] = injector.beaker.reagents.maximum_volume + data["reagentMin"] = 0 + else + data["reagentAmount"] = null + data["reagentMax"] = 1 + data["reagentMin"] = 0 + + if(injector.occupant) + data["occupantHealth"] = injector.occupant.health + data["occupantHealthMax"] = injector.occupant.maxHealth + else + data["occupantHealth"] = null + data["occupantHealthMax"] = null + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "xenobio_computer.tmpl", "Injector Control Console UI", 470, 450) + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/xenobio2/Topic(href, href_list) + + if(..()) + return 1 + + if(href_list["inject_target"]) + active = 1 + spawn(5) + injector.inject_reagents() + active = 0 + if(href_list["eject_occupant"]) + injector.eject_contents() + + if(href_list["eject_beaker"]) + injector.eject_beaker() + + usr.set_machine(src) + src.add_fingerprint(usr) + +/obj/item/weapon/circuitboard/xenobio2computer + name = T_BOARD("injector control console") + build_path = "/obj/item/weapon/circuitboard/xenobio2computer" + board_type = "computer" + origin_tech = list() //To be filled, diff --git a/code/modules/xenobio2/machinery/slime_replicator.dm b/code/modules/xenobio2/machinery/slime_replicator.dm new file mode 100644 index 0000000000..3626304bfe --- /dev/null +++ b/code/modules/xenobio2/machinery/slime_replicator.dm @@ -0,0 +1,149 @@ +/* + Here lives the slime replicator + This machine consumes cores to create a slime. + To create more of these cores, stick the slime core in the extractor. +*/ +/obj/machinery/slime/replicator + name = "slime core growth apparatus" + desc = "A machine for creating slimes from cores. Amazing!" + icon = 'icons/obj/xenoarchaeology.dmi' + icon_state = "restruct_0" + density = 1 + anchored = 1 + circuit = /obj/item/weapon/circuitboard/slimereplicator + var/obj/item/xenoproduct/slime/core/core = null + var/inuse + var/occupiedcolor = "#22FF22" + var/emptycolor = "#FF2222" + var/operatingcolor = "#FFFF22" + +/obj/machinery/slime/replicator/New() + ..() + circuit = new circuit(src) + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + component_parts += new /obj/item/weapon/stock_parts/matter_bin(src) + component_parts += new /obj/item/weapon/stock_parts/micro_laser(src) + RefreshParts() + update_light_color() + + +/obj/machinery/slime/replicator/attackby(var/obj/item/W, var/mob/user) + //Let's try to deconstruct first. + if(istype(W, /obj/item/weapon/screwdriver) && !inuse) + default_deconstruction_screwdriver(user, W) + return + + if(istype(W, /obj/item/weapon/crowbar)) + default_deconstruction_crowbar(user, W) + return + + var/obj/item/xenoproduct/slime/core/G = W + + if(!istype(G)) + return ..() + + if(core) + user << "[src] is already filled!" + return + if(panel_open) + user << "Close the panel first!" + core = G + user.drop_from_inventory(G) + G.forceMove(src) + update_light_color() + +/obj/machinery/slime/replicator/proc/update_light_color() + if(src.core && !(inuse)) + set_light(2, 2, occupiedcolor) + else if(src.core) + set_light(2, 2, operatingcolor) + else + set_light(2, 2, emptycolor) + +/obj/machinery/slime/replicator/proc/replicate_slime() + if(!src.core) + src.visible_message("\icon[src] [src] pings unhappily.") + else if(inuse) + return + + inuse = 1 + update_light_color() + icon_state = "restruct_1" + spawn(30) + var/mob/living/simple_animal/xeno/slime/S = new(src) + S.traitdat = core.traits + S.nameVar = core.nameVar + S.name = "[S.nameVar] baby slime" + S.ProcessTraits() + qdel(core) + spawn(30) + inuse = 0 + eject_slime() + icon_state = "restruct_0" + update_light_color() + src.updateUsrDialog() + +/obj/machinery/slime/replicator/proc/eject_slime() + for(var/mob/thing in contents) + thing.forceMove(loc) + +/obj/machinery/slime/replicator/proc/eject_core() + if(core) + core.forceMove(loc) + core = null + +/obj/machinery/slime/replicator/proc/eject_contents() + eject_slime() + eject_core() + +//Here lies the UI +/obj/machinery/slime/replicator/attack_hand(mob/user as mob) + user.set_machine(src) + interact(user) + +/obj/machinery/slime/replicator/interact(mob/user as mob) + var/dat = "" + if(!inuse) + dat = {" + Slime core container holds:
+ [core]
+ "} + if (core && !(stat & (NOPOWER|BROKEN))) + dat += "Start the replication process
" + if(core) + dat += "Eject the core
" + else + dat += "Please wait..." + var/datum/browser/popup = new(user, "Slime Replicator", "Slime Replicator", src) + popup.set_content(dat) + popup.open() + return + + +/obj/machinery/slime/replicator/Topic(href, href_list) + if(..()) + return + usr.set_machine(src) + switch(href_list["action"]) + if ("replicate") + replicate_slime() + if("eject") + eject_core() + src.updateUsrDialog() + return + +//Circuit board below, +/obj/item/weapon/circuitboard/slimereplicator + name = T_BOARD("Slime replicator") + build_path = "/obj/machinery/slime/replicator" + board_type = "machine" + origin_tech = list(TECH_DATA = 3, TECH_BIO = 3) + req_components = list( + /obj/item/weapon/stock_parts/manipulator = 2, + /obj/item/weapon/stock_parts/matter_bin = 1, + /obj/item/weapon/stock_parts/micro_laser = 1 + ) + + \ No newline at end of file diff --git a/code/modules/xenobio2/mob/slime/slime life.dm b/code/modules/xenobio2/mob/slime/slime life.dm new file mode 100644 index 0000000000..4853454476 --- /dev/null +++ b/code/modules/xenobio2/mob/slime/slime life.dm @@ -0,0 +1,55 @@ +/* +Slime specific life events go here. +*/ +#define HAPPYLEVEL 200 +#define ANGRYLEVEL 10 +/mob/living/simple_animal/xeno/slime/Life() + . = ..() + if(..()) + if(health) + if(is_child) + if(nutrition >= 275) + growthcounter++ + nutrition -= hunger_factor + if(nutrition <= 75) + growthcounter-- + if(growthcounter >= growthpoint) + src.GrowUp() + + else + if(nutrition < 0) + nutrition = 0 + if((prob(10) && !emote_on)) //Slimes might display their food-based emotions over time. + var/image/I = new(src.icon) + I.color = "#FFFFFF" + I.layer = src.layer + 0.2 + if((nutrition >= HAPPYLEVEL)) + if((nutrition >= 1.5 * HAPPYLEVEL)) + I.icon_state = "aslime-:33" + else + I.icon_state = "aslime-:3" + + else if((nutrition > ANGRYLEVEL)) + if((nutrition >= 10 * ANGRYLEVEL)) + I.icon_state = "aslime-pout" + else if((nutrition >= 5 * ANGRYLEVEL)) + I.icon_state = "aslime-sad" + else + I.icon_state = "aslime-angry" + overlays += I + emote_on = 1 + spawn(30) + GenerateAdultIcon() + emote_on = null + + else + if(is_child) + icon_state = "slime baby dead" + else + overlays.Cut() + icon_state = "slime adult dead" + color = traitdat.traits[TRAIT_XENO_COLOR] + + return 0 //Everything worked okay + + return //xeno/Life() returned 0. \ No newline at end of file diff --git a/code/modules/xenobio2/mob/slime/slime procs.dm b/code/modules/xenobio2/mob/slime/slime procs.dm new file mode 100644 index 0000000000..56ad5d6050 --- /dev/null +++ b/code/modules/xenobio2/mob/slime/slime procs.dm @@ -0,0 +1,114 @@ +/* +Slime specific procs go here. +*/ +#define SHINYOVERLAY 0 +#define LIGHTOVERLAY 1 +#define MAXOVERLAY 2 //Should be 1 + last overlay, to give the chance for matte slimes + +/mob/living/simple_animal/xeno/slime/RandomizeTraits() + traitdat.traits[TRAIT_XENO_COLDRES] = rand(30,270) + traitdat.traits[TRAIT_XENO_HEATRES] = rand(30,270) + traitdat.traits[TRAIT_XENO_CHEMVOL] = round(rand(20,40)) //Wow, a slime core with the capacity to hold 2/3rd's a beaker's worth of chemicals. + traitdat.traits[TRAIT_XENO_HEALTH] = round(rand(50, 75)) + traitdat.traits[TRAIT_XENO_HUNGER] = rand(1, 20) + traitdat.traits[TRAIT_XENO_STARVEDAMAGE] = rand(1, 4) + traitdat.traits[TRAIT_XENO_EATS] = prob(95) //Odds are, that thing'll need to eat. + traitdat.traits[TRAIT_XENO_CHROMATIC] = prob(5) + if(traitdat.traits[TRAIT_XENO_CHROMATIC]) + traitdat.traits[TRAIT_XENO_HOSTILE] = 0 + else + traitdat.traits[TRAIT_XENO_HOSTILE] = prob(30) + traitdat.traits[TRAIT_XENO_GLOW_STRENGTH] = round(rand(1,3)) + traitdat.traits[TRAIT_XENO_GLOW_RANGE] = round(rand(1,3)) + traitdat.traits[TRAIT_XENO_STRENGTH] = round(rand(4,9)) + traitdat.traits[TRAIT_XENO_STR_RANGE] =round(rand(0,2)) + traitdat.traits[TRAIT_XENO_CANLEARN] = prob(68) + traitdat.traits[TRAIT_XENO_SPEED] = round(rand(-10,10)) + + + +/mob/living/simple_animal/xeno/slime/proc/GrowUp() + GenerateAdult() + + maxHealth = traitdat.get_trait(TRAIT_XENO_HEALTH) + health = maxHealth + is_child = 0 + + return 1 + +/mob/living/simple_animal/xeno/slime/Mutate() + ..() + cores = round(rand(1,9)) + if(is_child) + RandomizeTraits() + GenerateChild() + else + GenerateAdult() + +/mob/living/simple_animal/xeno/slime/proc/GenerateChild() + overlays.Cut() + name = "[nameVar] baby slime" + real_name = "[nameVar] baby slime" + desc = "A shifting blob of [nameVar] goo." + icon_state = "slime baby" + icon_living = "slime baby" + icon_dead = "slime baby dead" + color = traitdat.traits[TRAIT_XENO_COLOR] + maxHealth = traitdat.traits[TRAIT_XENO_HEALTH]/2 + health = maxHealth + + return 1 + +/mob/living/simple_animal/xeno/slime/proc/GenerateAdult() + overlays.Cut() + name = "[nameVar] slime" + real_name = "[nameVar] slime" + desc = "A shifting mass of [nameVar] goo." + color = "" + icon_state = "" + overlay = round(rand(0, MAXOVERLAY)) + GenerateAdultIcon() + +/mob/living/simple_animal/xeno/slime/proc/GenerateAdultIcon() //Hack and slash adventure game to make slimes have no color on light effects later + overlays.Cut() + var/image/Img = new(src.icon) + Img.icon_state = "slime adult" + Img.color = traitdat.traits[TRAIT_XENO_COLOR] + Img.layer = src.layer + overlays += Img + + switch(overlay) + if(SHINYOVERLAY) + var/image/I = new(src.icon) + I.icon = src.icon + I.icon_state = "slime shiny" + I.layer = src.layer + 0.1 + I.color = "#FFFFFF" + overlays += I + if(LIGHTOVERLAY) + var/image/I = new(src.icon) + I.icon = src.icon + I.icon_state = "" + I.icon_state = "slime light" + I.layer = src.layer + 0.1 + I.color = "#FFFFFF" + overlays += I + +/mob/living/simple_animal/xeno/slime/handle_reagents() + if(!stasis) + if(!reagents) + return + if(reagents.total_volume <= 0) + return + if(reagents.has_reagent("docilitytoxin")) //Toxin that makes them docile? Good for quelling angry mobs. + hostile = 0 + traitdat.traits[TRAIT_XENO_HOSTILE] = 0 + ..() + +/mob/living/simple_animal/xeno/slime/ProcessTraits() + ..() + if(is_child) + GenerateChild() + else + GenerateAdult() + \ No newline at end of file diff --git a/code/modules/xenobio2/mob/slime/slime.dm b/code/modules/xenobio2/mob/slime/slime.dm new file mode 100644 index 0000000000..b214b34630 --- /dev/null +++ b/code/modules/xenobio2/mob/slime/slime.dm @@ -0,0 +1,92 @@ +/* +Slime definitions, Life and New live here. +*/ +/mob/living/simple_animal/xeno/slime //Adult values are found here + nameVar = "grey" //When mutated, nameVar might change. + desc = "A shifting, mass of goo." + speak_emote = list("garbles", "chirps", "blurbles") + colored = 1 + color = "#CACACA" + icon = 'icons/mob/slime2.dmi' + icon_state = "slime adult" + icon_living = "slime adult" + icon_dead = "slime adult dead" + internal_vol = 200 + mut_max = 50 + mutable = COLORMUT + var/is_child = 1 + var/cores = 3 + var/growthcounter = 0 + var/growthpoint = 50 //At what point they grow up. + var/shiny = 0 + move_to_delay = 17 //Slimes shouldn't be able to go faster than humans. + default_chems = list("slimejelly" = 5) + attacktext = "absorbed some of" + response_help = "pats" + response_disarm = "tries to stop" + response_harm = "hits" + + var/emote_on = null + + maleable = MAX_MALEABLE + + //Slimes can speak all of the languages, oh no! + universal_speak = 1 + speak_chance = 1 + speak = list("Hello?", + "Where is this going?", + "What is that?", + "What is in the box?", + "Cargo.", + "Transport?", + "Special?", + "Slime?") + + //Overlay information + var/overlay = 1 // 1 = normal lighting, 0 = shiny, 2 = too shiny, -1 = no overlay + + chemreact = list( "nutriment" = list("nutr" = 0.5), + "radium" = list("toxic" = 0.3, "mut" = 1), + "mutagen" = list("nutr" = 0.4, "mut" = 2), + "water" = list("nutr" = -0.1), + "milk" = list("nutr" = 0.3), + "sacid" = list("toxic" = 1), + "pacid" = list("toxic" = 2), + "chlorine" = list("toxic" = 0.5), + "ammonia" = list("toxic" = 0.5), + "sodawater" = list("toxic" = 0.1, "nutr" = -0.1), + "beer" = list("nutr" = 0.6), + "diethylamine" = list("nutr" = 0.9), + "sugar" = list("toxic" = 0.4, "nutr" = 0.2), + "eznutrient" = list("nutr" = 0.8), + "cryoxadone" = list("toxic" = 0.4), + "flourine" = list("toxic" = 0.1), + "robustharvest" = list("nutr" = 1.5), + "glucose" = list("nutr" = 0.5), + "blood" = list("nutr" = 0.75, "toxic" = 0.05, "mut" = 0.45), + "fuel" = list("toxic" = 0.4), + "toxin" = list("toxic" = 0.5), + "carpotoxin" = list("toxic" = 1, "mut" = 1.5), + "phoron" = list("toxic" = 1.5, "mut" = 0.03), + "cyanide" = list("toxic" = 3.5), + "slimejelly" = list("nutr" = 0.5), + "amutationtoxin" = list("toxic" = 0.1, "heal" = 1.5, "mut" = 3), + "mutationtoxin" = list("toxic" = 0.1, "heal" = 1, "mut" = 1.5), + "gold" = list("heal" = 0.3, "nutr" = 0.7, "mut" = 0.3), + "uranium" = list("heal" = 0.3, "toxic" = 0.7, "mut" = 1.2), + "glycerol" = list("nutr" = 0.6), + "woodpulp" = list("heal" = 0.1, "nutr" = 0.7), + "docilitytoxin" = list("nutr" = 0.3) ) + +/mob/living/simple_animal/xeno/slime/New() + ..() + for(var/datum/language/L in (typesof(/datum/language) - /datum/language)) + languages += L + speak += "[station_name()]?" + traitdat.source = "Slime" + resistances[BURN] = 4 + resistances[BRUTE] = 0.2 + resistances[TOX] = 1.5 + GenerateChild() + return 1 + \ No newline at end of file diff --git a/code/modules/xenobio2/mob/slime/slime_core.dm b/code/modules/xenobio2/mob/slime/slime_core.dm new file mode 100644 index 0000000000..fff19a231f --- /dev/null +++ b/code/modules/xenobio2/mob/slime/slime_core.dm @@ -0,0 +1,12 @@ +/* +Slime core lives here. +*/ +/obj/item/xenoproduct/slime/core + name = "slime core" + desc = "Gooey." + icon = 'icons/mob/slime2.dmi' + icon_state = "slime extract" + source = "Slime" + product = "core" + + \ No newline at end of file diff --git a/code/modules/xenobio2/mob/slime/slime_monkey.dm b/code/modules/xenobio2/mob/slime/slime_monkey.dm new file mode 100644 index 0000000000..f302f9104a --- /dev/null +++ b/code/modules/xenobio2/mob/slime/slime_monkey.dm @@ -0,0 +1,65 @@ +/* +Slime cube lives here. +*/ +/obj/item/slime_cube + name = "slimy monkey cube" + desc = "Wonder what might come out of this." + icon = 'icons/mob/slime2.dmi' + icon_state = "slime cube" + var/searching = 0 + +/obj/item/slime_cube/attack_self(mob/user as mob) + if(!searching) + user << "You stare at the slimy cube, watching as some activity occurs." + icon_state = "slime cube active" + searching = 1 + request_player() + spawn(600) reset_search() + +/obj/item/slime_cube/proc/request_player() + for(var/mob/observer/dead/O in player_list) + if(!O.MayRespawn()) + continue + if(O.client) + if(O.client.prefs.be_special & BE_ALIEN) + question(O.client) + +/obj/item/slime_cube/proc/question(var/client/C) + spawn(0) + if(!C) return + var/response = alert(C, "Someone is requesting a soul for a promethean. Would you like to play as one?", "Promethean request", "Yes", "No", "Never for this round") + if(response == "Yes") + response = alert(C, "Are you sure you want to play as a promethean?", "Promethean request", "Yes", "No") + if(!C || 2 == searching) return //handle logouts that happen whilst the alert is waiting for a response, and responses issued after a brain has been located. + if(response == "Yes") + transfer_personality(C.mob) + else if (response == "Never for this round") + C.prefs.be_special ^= BE_ALIEN + +/obj/item/slime_cube/proc/reset_search() //We give the players sixty seconds to decide, then reset the timer. + icon_state = "slime cube" + if(searching == 1) + searching = 0 + var/turf/T = get_turf_or_move(src.loc) + for (var/mob/M in viewers(T)) + M.show_message("The activity in the cube dies down. Maybe it will spark another time.") + +/obj/item/slime_cube/proc/transfer_personality(var/mob/candidate) + announce_ghost_joinleave(candidate, 0, "They are a promethean now.") + src.searching = 2 + var/mob/living/carbon/human/S = new(get_turf(src)) + S.client = candidate.client + S. << "You are a promethean, brought into existence on [station_name()]." + S.mind.assigned_role = "Promethean" + S.set_species("Promethean") + S.shapeshifter_set_colour("#05FF9B") + for(var/mob/M in viewers(get_turf_or_move(loc))) + M.show_message("The monkey cube suddenly takes the shape of a humanoid!") + var/newname = sanitize(input(S, "You are a Promethean. Would you like to change your name to something else?", "Name change") as null|text, MAX_NAME_LEN) + if (newname) + S.real_name = newname + S.name = S.real_name + S.dna.real_name = newname + if(S.mind) S.mind.name = S.name + qdel(src) + \ No newline at end of file diff --git a/code/modules/xenobio2/mob/xeno procs.dm b/code/modules/xenobio2/mob/xeno procs.dm new file mode 100644 index 0000000000..e20373c21c --- /dev/null +++ b/code/modules/xenobio2/mob/xeno procs.dm @@ -0,0 +1,167 @@ +/* +Basic definition of creatures for Xenobiology + +Includes: +Proc for metabolism +Proc for mutating +Procs for copying speech, if applicable +Procs for targeting +*/ +/mob/living/simple_animal/xeno/proc/ProcessTraits() + if(maleable >= MAX_MALEABLE) + maxHealth = traitdat.get_trait(TRAIT_XENO_HEALTH) + health = maxHealth + if(traitdat.chemlist.len) //Let's make sure that this has a length. + chemreact = traitdat.chemlist + else + traitdat.chemlist = chemreact + chromatic = traitdat.traits[TRAIT_XENO_CHROMATIC] + if(maleable >= MINOR_MALEABLE) + if(colored) + color = traitdat.traits[TRAIT_XENO_COLOR] + speed = traitdat.traits[TRAIT_XENO_SPEED] + hunger_factor = traitdat.traits[TRAIT_XENO_HUNGER] + starve_damage = traitdat.traits[TRAIT_XENO_STARVEDAMAGE] + minbodytemp = T20C - traitdat.traits[TRAIT_XENO_COLDRES] + maxbodytemp = T20C + traitdat.traits[TRAIT_XENO_HEATRES] + + if(traitdat.traits[TRAIT_XENO_BIOLUMESCENT]) + set_light(traitdat.traits[TRAIT_XENO_GLOW_RANGE], traitdat.traits[TRAIT_XENO_GLOW_STRENGTH], traitdat.traits[TRAIT_XENO_BIO_COLOR]) + else + set_light(0, 0, "#000000") //Should kill any light that shouldn't be there. + if(chromatic) + hostile = 0 //No. No laser-reflecting hostile creatures. Bad. + else + hostile = traitdat.traits[TRAIT_XENO_HOSTILE] + + speed = traitdat.traits[TRAIT_XENO_SPEED] + + if(traitdat.traits[TRAIT_XENO_CANSPEAK]) + speak_chance = traitdat.traits[TRAIT_XENO_SPEAKCHANCE] + else + speak_chance = 0 + + melee_damage_lower = traitdat.get_trait(TRAIT_XENO_STRENGTH) - traitdat.get_trait(TRAIT_XENO_STR_RANGE) + melee_damage_upper = traitdat.traits[TRAIT_XENO_STRENGTH] + traitdat.get_trait(TRAIT_XENO_STR_RANGE) + + + +//Metabolism proc, simplified for xenos. Heavily based on botanical metabolism. +/mob/living/simple_animal/xeno/proc/handle_reagents() + if(!stasis) + if(!reagents) + return + + //Let's handle some chemical smoke, for scientific smoke bomb purposes. + for(var/obj/effect/effect/smoke/chem/smoke in view(1, src)) + if(smoke.reagents.total_volume) + smoke.reagents.trans_to_mob(src, 10, CHEM_BLOOD, copy = 1) + + reagents.trans_to_obj(temp_chem_holder, min(reagents.total_volume,rand(1,4))) + var/reagent_total + var/list/reagent_response = list() + for(var/datum/reagent/R in temp_chem_holder.reagents.reagent_list) + + reagent_total = temp_chem_holder.reagents.get_reagent_amount(R.id) + + reagent_response = chemreact[R.id] + + if(reagent_response["toxic"]) + adjustToxLoss(reagent_response["toxic"] * reagent_total) + + if(reagent_response["heal"]) + heal_overall_damage(reagent_response["heal"] * reagent_total) + + if(reagent_response["nutr"]) + nutrition += reagent_response["nutr"] * reagent_total + + if(reagent_response["mut"]) + mut_level += reagent_response["mut"] * reagent_total + + temp_chem_holder.reagents.clear_reagents() + + return 1 //Everything worked out okay. + + return 0 + +/mob/living/simple_animal/xeno/proc/Mutate() + nameVar = "mutated" + if((COLORMUT & mutable)) + traitdat.traits[TRAIT_XENO_COLOR] = "#" + for(var/i=0, i<6, i++) + traitdat.traits[TRAIT_XENO_COLOR] += pick(hexNums) + traitdat.traits[TRAIT_XENO_BIO_COLOR] = "#" + for(var/i=0, i<6, i++) + traitdat.traits[TRAIT_XENO_BIO_COLOR] += pick(hexNums) + + RandomChemicals() + //if(SPECIESMUT & mutable) + //Placeholder, currently no xenos that have species mutations. + RandomizeTraits() + ProcessTraits() + return 1 + +/mob/living/simple_animal/xeno/proc/RandomizeTraits() + return + +/mob/living/simple_animal/xeno/hear_say(var/message, var/verb = "says", var/datum/language/language, var/alt_name = "",var/italics = 0, var/mob/speaker = null) + if(traitdat.traits[TRAIT_XENO_CANLEARN]) + /* + Until this gets sorted out to a functioning point, or waiting on Psi's saycode update. + var/learned_message = say_understands(speaker, language) + if(!message || isxeno(speaker)) + return + if(learned_message) //Is it understood? + var/complete_message = ",[language.key] [message]" + if(!(complete_message in speak)) + speech_buffer.Add(complete_message) + log_debug("Added '[complete_message]'.") + else + */ + if(!(message in speak)) + speech_buffer.Add(message) + ..(message,verb,language,alt_name,italics,speaker) + +/mob/living/simple_animal/xeno/proc/ProcessSpeechBuffer() + if(speech_buffer.len) + if(prob(traitdat.get_trait(TRAIT_XENO_LEARNCHANCE)) && traitdat.get_trait(TRAIT_XENO_CANLEARN)) + var/chosen = pick(speech_buffer) + speak.Add(chosen) + /* Uncoment for logging of speech list. + log_debug("Added [chosen] to speak list.") + log_debug("Speechlist cut.") */ + speech_buffer.Cut() +// +/mob/living/simple_animal/xeno/proc/BuildReagentLists() + return + +/mob/living/simple_animal/xeno/bullet_act(var/obj/item/projectile/P) + //Shamelessly stolen from ablative armor. + if((traitdat.traits[TRAIT_XENO_CHROMATIC]) && istype(P, /obj/item/projectile/beam)) + visible_message(")\The beam reflects off of the [src]!") + // Find a turf near or on the original location to bounce to + var/new_x = P.starting.x + pick(0, -1, 1, -2, 2) + var/new_y = P.starting.y + pick(0, -1, 1, -2, 2) + var/turf/curloc = get_turf(src) + + // redirect the projectile + P.redirect(new_x, new_y, curloc, src) + + return -1 + + else + ..() + +/mob/living/simple_animal/xeno/proc/RandomChemicals() + traitdat.chems.Cut() //Clear the amount first. + + var/num_chems = round(rand(1,4)) + var/list/chemchoices = xenoChemList + + for(var/i = 1 to num_chems) + var/chemtype = pick(chemchoices) + chemchoices -= chemtype + var/chemamount = rand(1,5) + traitdat.chems[chemtype] = chemamount + + traitdat.chems += default_chems diff --git a/code/modules/xenobio2/mob/xeno.dm b/code/modules/xenobio2/mob/xeno.dm new file mode 100644 index 0000000000..7b1bf7dfac --- /dev/null +++ b/code/modules/xenobio2/mob/xeno.dm @@ -0,0 +1,97 @@ +//First we define certain flags, can be expanded upon later. + +/* +Basic definition of creatures for Xenobiology +Also includes Life and New +*/ + +/mob/living/simple_animal/xeno + name = "Xeno" + real_name = "Xeno" + desc = "Something's broken, yell at someone." + melee_damage_lower = 0 + melee_damage_upper = 0 + attacktext = "hit" + attack_sound = null + friendly = "touches" + environment_smash = 0 + + var/datum/xeno/traits/traitdat //Trait datum. + + var/internal_vol = 1000 //Internal volume for ingesting/injected reagents + + var/obj/temp_chem_holder //Used in handle_reagents() + + var/mutable = NOMUT //Flag for mutation. + var/nameVar = "Blah" + var/instability = 0 + var/stasis = 0 + var/mut_level = 0 //How mutated a specimen is. Irrelevant while mutable = NOMUT. + var/mut_max = 100000 + var/colored = 1 + var/maleable = MIN_MALEABLE //How easy is it to manipulate traitdat.traits after it's alive. + var/stability = 0 //Used in gene manipulation + + //Traits that might not be maleable. + var/list/chemreact = list() + var/hunger_factor = 10 + var/chromatic = 0 + var/starve_damage = 0 + + //Used for speech learning + var/list/speech_buffer = list() + + var/list/default_chems = list() + + +//Life additions +/mob/living/simple_animal/xeno/Life() + if(src.stat == DEAD) + return 0 + + if(stasis) + stasis-- + if(stasis < 0) + stasis = 0 + return 0 + ..() + handle_reagents() + if((mut_level >= mut_max) && !(mutable & NOMUT)) + Mutate() + mut_level -= mut_max + + ProcessSpeechBuffer() + + //Have to feed the xenos somehow. + if(nutrition < 0) + nutrition = 0 + if((nutrition > 0 ) && traitdat.traits[TRAIT_XENO_EATS]) + if(nutrition >= 300) + nutrition -= hunger_factor + else + if(traitdat.traits[TRAIT_XENO_EATS]) + health = starve_damage + + return 1 //Everything worked okay. + +/mob/living/simple_animal/xeno/New() + + traitdat = new() + + ProcessTraits() + + ..() + if(colored) + color = traitdat.get_trait(TRAIT_XENO_COLOR) + create_reagents(internal_vol) + temp_chem_holder = new() + temp_chem_holder.create_reagents(20) + nutrition = 350 + for(var/R in default_chems) + traitdat.chems[R] = default_chems[R] + + traitdat.source = name + + if(!health) + stat = DEAD + \ No newline at end of file diff --git a/code/modules/xenobio2/mob/xeno_product.dm b/code/modules/xenobio2/mob/xeno_product.dm new file mode 100644 index 0000000000..b084b8eea6 --- /dev/null +++ b/code/modules/xenobio2/mob/xeno_product.dm @@ -0,0 +1,13 @@ +/* +Xenobiological product lives here as a basic type. +*/ +/obj/item/xenoproduct + name = "Xenobiological product" + desc = "How did this get here?." + icon = 'icons/mob/slime2.dmi' + icon_state = "slime extract" + var/datum/xeno/traits/traits + var/source = "Unknown" + var/product = "mess" + var/nameVar = "blah" + \ No newline at end of file diff --git a/code/modules/xenobio2/tools/slime_handling_tools.dm b/code/modules/xenobio2/tools/slime_handling_tools.dm new file mode 100644 index 0000000000..b8f37a106f --- /dev/null +++ b/code/modules/xenobio2/tools/slime_handling_tools.dm @@ -0,0 +1,64 @@ +/* What this file contains: + + * A specialized stun prod, for handling fiesty slimes + + * A specialized stun gun, for handling many fiesty slimes + + * A stun projectile for handling xenomorphs. + +*/ +/obj/item/weapon/melee/baton/slime + name = "slimebaton" + desc = "A modified stun baton designed to stun slimes for handling." + icon_state = "slimebaton" + item_state = "slimebaton" + slot_flags = SLOT_BELT + force = 9 + lightcolor = "#33CCFF" + origin_tech = list(TECH_COMBAT = 2, TECH_BIO = 4) + agonyforce = 10 //It's not supposed to be great at stunning human beings. + var/stasisforce = 60 //How much stasis it does to slimes, and 1/3rd to non-slimes. + +/obj/item/weapon/melee/baton/slime/attack(mob/M, mob/user) + if(istype(M, /mob/living/simple_animal/xeno)) + var/mob/living/simple_animal/xeno/X = M + if(istype(M, /mob/living/simple_animal/xeno/slime)) + X.stasis += stasisforce + else + X.stasis += (stasisforce / 6) + ..() + +/obj/item/weapon/melee/baton/slime/loaded/New() + ..() + bcell = new/obj/item/weapon/cell/high(src) + update_icon() + return + + +// Xeno stun gun + projectile +/obj/item/weapon/gun/energy/taser/xeno + name = "xeno taser gun" + desc = "Straight out of NT's testing laboratories, this small gun is used to subdue non-humanoid xeno life forms. While marketed towards handling slimes, it may be useful for other creatures." + icon_state = "taserold" + fire_sound = 'sound/weapons/taser2.ogg' + max_shots = 10 + projectile_type = /obj/item/projectile/beam/stun/xeno + +/obj/item/projectile/beam/stun/xeno + icon_state = "omni" + agony = 4 + var/stasisforce = 40 + + muzzle_type = /obj/effect/projectile/laser_omni/muzzle + tracer_type = /obj/effect/projectile/laser_omni/tracer + impact_type = /obj/effect/projectile/laser_omni/impact + +/obj/item/projectile/beam/stun/xeno/on_hit(var/atom/target, var/blocked = 0) + if(istype(target, /mob/living/simple_animal/xeno)) + var/mob/living/simple_animal/xeno/X = target + if(istype(target, /mob/living/simple_animal/xeno/slime)) + X.stasis += stasisforce + else + X.stasis += (stasisforce / 8) + else + ..() \ No newline at end of file diff --git a/code/modules/xenobio2/tools/xeno_trait_scanner.dm b/code/modules/xenobio2/tools/xeno_trait_scanner.dm new file mode 100644 index 0000000000..8063b6071b --- /dev/null +++ b/code/modules/xenobio2/tools/xeno_trait_scanner.dm @@ -0,0 +1,185 @@ +/obj/item/device/analyzer/xeno_analyzer + name = "exotic biological analyzer" + desc = "A device to investigate the genetic data of a biological target." + var/form_title + var/last_data + +/obj/item/device/analyzer/xeno_analyzer/proc/print_report_verb() + set name = "Print Plant Report" + set category = "Object" + set src = usr + + if(usr.stat || usr.restrained() || usr.lying) + return + print_report(usr) + +/obj/item/device/analyzer/xeno_analyzer/Topic(href, href_list) + if(..()) + return + if(href_list["print"]) + print_report(usr) + +/obj/item/device/analyzer/xeno_analyzer/proc/print_report(var/mob/living/user) + if(!last_data) + user << "There is no scan data to print." + return + var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(get_turf(src)) + P.name = "paper - [form_title]" + P.info = "[last_data]" + if(istype(user,/mob/living/carbon/human)) + user.put_in_hands(P) + user.visible_message("\The [src] spits out a piece of paper.") + return + +/obj/item/device/analyzer/xeno_analyzer/attack_self(mob/user as mob) + print_report(user) + return 0 + +/obj/item/device/analyzer/xeno_analyzer/afterattack(var/target, mob/user, flag) + if(!flag) return + + var/datum/xeno/traits/trait_info + var/datum/reagents/prod_reagents + var/targetName + var/growth_level + var/growth_max + if(istype(target,/obj/structure/table)) + return ..() + else if(istype(target,/mob/living/simple_animal/xeno)) + + var/mob/living/simple_animal/xeno/X = target + if(istype(X, /mob/living/simple_animal/xeno/slime)) + var/mob/living/simple_animal/xeno/slime/S = X + if(S.is_child) + growth_level = S.growthcounter + growth_max = S.growthpoint + + targetName = X.name + trait_info = X.traitdat + + else if(istype(target,/obj/item/xenoproduct)) + + var/obj/item/xenoproduct/P = target + trait_info = P.traits + targetName = P.name + prod_reagents = P.reagents + + if(!trait_info) + user << "[src] can tell you nothing about \the [target]." + return + + form_title = "[targetName]" + var/dat = "

Biological data for [form_title]

" + user.visible_message("[user] runs the scanner over \the [target].") + + dat += "

General Data

" + + + dat += "Health: [trait_info.get_trait(TRAIT_XENO_HEALTH)]" + + if(prod_reagents && prod_reagents.reagent_list && prod_reagents.reagent_list.len) + dat += "

Reagent Data

" + + dat += "
This sample contains: " + for(var/datum/reagent/R in prod_reagents.reagent_list) + dat += "
- [R.name], [prod_reagents.get_reagent_amount(R.id)] unit(s)" + + dat += "

Other Data

" + + if(trait_info.get_trait(TRAIT_XENO_EATS)) + dat += "This subject requires nutritional intake.
" + + if(trait_info.get_trait(TRAIT_XENO_HUNGER)) + if(trait_info.get_trait(TRAIT_XENO_HUNGER) < 7) + dat += "It appears to have a slower metabolism.
" + else if(trait_info.get_trait(TRAIT_XENO_HUNGER) > 15) + dat += "It appears to have a greater metabolism.
" + else + dat += "It appears to have an average metabolism.
" + + if(trait_info.get_trait(TRAIT_IMMUTABLE) == -1) + dat += "This plant is highly mutable.
" + else if(trait_info.get_trait(TRAIT_IMMUTABLE) > 0) + dat += "This plant does not possess genetics that are alterable.
" + + if(trait_info.get_trait(TRAIT_XENO_HEALTH)) + if(trait_info.get_trait(TRAIT_XENO_HEALTH) < 20) + dat += "It bears characteristics that indicate resilience to damage.
" + else if(trait_info.get_trait(TRAIT_XENO_HEALTH) > 10) + dat += "It bears characteristics that indicate susceptibility to damage.
" + else + dat += "It bears no characters indicating resilience to damage.
" + + if(growth_max) + if(growth_level < 25) + dat += "It appears to be far to growing up.
" + else if(growth_level > 40) + dat += "It appears to be close to growing up.
" + else + dat += "It appears to be growing.
" + else + dat += "It appears to be fully grown.
" + + if(trait_info.get_trait(TRAIT_XENO_COLDRES)) + if(trait_info.get_trait(TRAIT_XENO_COLDRES) < 10) + dat += "It bears characteristics that indicate resilience to colder temperatures.
" + else if(trait_info.get_trait(TRAIT_XENO_COLDRES) > 20) + dat += "It bears characteristics that indicate susceptibility to colder temperatures.
" + + if(trait_info.get_trait(TRAIT_XENO_HEATRES)) + if(trait_info.get_trait(TRAIT_XENO_HEATRES) < 10) + dat += "It bears characteristics that indicate resilience to warmer temperatures.
" + else if(trait_info.get_trait(TRAIT_XENO_HEATRES) > 20) + dat += "It bears characteristics that indicate susceptibility to warmer temperatures.
" + + if(trait_info.get_trait(TRAIT_XENO_SPEED)) + if(trait_info.get_trait(TRAIT_XENO_SPEED) < 0) + dat += "It appears to be agile.
" + else if(trait_info.get_trait(TRAIT_XENO_SPEED) > 0) + dat += "It appears to be slower.
" + + if(trait_info.get_trait(TRAIT_XENO_CANSPEAK)) + dat += "
The subject appears to be able to articulate." + + if(trait_info.get_trait(TRAIT_XENO_SPEAKCHANCE)) + if(trait_info.get_trait(TRAIT_XENO_SPEAKCHANCE) < 10) + dat += "The subject appears to articulate infrequently.
" + else if(trait_info.get_trait(TRAIT_XENO_SPEAKCHANCE) > 20) + dat += "The subject appears to articulate frequently.
" + + if(trait_info.get_trait(TRAIT_XENO_CANLEARN)) + dat += "
The subject appears to have process verbal information." + + if(trait_info.get_trait(TRAIT_XENO_LEARNCHANCE)) + if(trait_info.get_trait(TRAIT_XENO_LEARNCHANCE) < 50) + dat += "The subject appears to comprehend verbal information infrequently.
" + else if(trait_info.get_trait(TRAIT_XENO_LEARNCHANCE) > 51) + dat += "The subject appears to comprehend verbal information frequently.
" + + if(trait_info.get_trait(TRAIT_XENO_STRENGTH) < 5) + dat += "It appears to have lower average physical capacity.
" + else if(trait_info.get_trait(TRAIT_XENO_STRENGTH) > 7) + dat += "It appears to have greater average physical capacity.
" + + if(trait_info.get_trait(TRAIT_XENO_STR_RANGE)) + if(trait_info.get_trait(TRAIT_XENO_STR_RANGE) < 50) + dat += "The subject appears to have more consistent attacks.
" + else if(trait_info.get_trait(TRAIT_XENO_STR_RANGE) > 51) + dat += "The subject appears to have more varied attacks.
" + + if(trait_info.get_trait(TRAIT_XENO_BIOLUMESCENT)) + dat += "
It is [trait_info.get_trait(TRAIT_XENO_BIO_COLOR) ? "bio-luminescent" : "bio-luminescent"]." + + if(trait_info.get_trait(TRAIT_XENO_HOSTILE)) + dat += "
The subject appears to have hostile tendencies." + + if(trait_info.get_trait(TRAIT_XENO_CHROMATIC)) + dat += "
The subject appears to have chromatic particles inside of it." + + + if(dat) + last_data = dat + dat += "

\[print report\]" + user << browse(dat,"window=xeno_analyzer") + + return \ No newline at end of file diff --git a/code/unit_tests/map_tests.dm b/code/unit_tests/map_tests.dm index 97d4909833..f8f735f867 100644 --- a/code/unit_tests/map_tests.dm +++ b/code/unit_tests/map_tests.dm @@ -51,7 +51,7 @@ if(!A.air_vent_info.len && !(A.type in exempt_from_atmos)) log_unit_test("[bad_msg] lacks an Air vent.") area_good = 0 - + if(!area_good) bad_areas.Add(A) @@ -97,3 +97,17 @@ pass("All \[[wire_test_count]\] wires had no overlapping cables going the same direction.") return 1 + +/datum/unit_test/active_edges + name = "MAP: Active edges (all maps)" + +/datum/unit_test/active_edges/start_test() + + var/active_edges = air_master.active_edges.len + + if(active_edges) + fail("Maps contained [active_edges] active edges at round-start.") + else + pass("No active edges.") + + return 1 diff --git a/code/world.dm b/code/world.dm index bfa399fe2a..66edcd00d2 100644 --- a/code/world.dm +++ b/code/world.dm @@ -73,6 +73,9 @@ var/global/datum/global_init/init = new () // Set up roundstart seed list. plant_controller = new() + + // Set up roundstart gene masking + xenobio_controller = new() // This is kinda important. Set up details of what the hell things are made of. populate_material_list() diff --git a/html/changelog.html b/html/changelog.html index 8a5a9b6746..e5cdf2a7a2 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -53,6 +53,46 @@ -->
+

30 May 2016

+

EmperorJon updated:

+ + +

29 May 2016

+

Datraen updated:

+ +

Zuhayr updated:

+ + +

27 May 2016

+

Yoshax updated:

+ + +

25 May 2016

+

Serithi updated:

+ +

Yoshax updated:

+ +

17 May 2016

SilveryFerret updated: