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/mobs.dm b/code/__defines/mobs.dm index df633d1ae5..68b313d9a1 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -26,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 @@ -185,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/_helpers/game.dm b/code/_helpers/game.dm index d5a7b1ed46..57ff4b7013 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -248,31 +248,31 @@ . |= 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/list/results = list("objs" = list(), "mobs" = list()) +/proc/get_mobs_and_objs_in_view_fast(var/turf/T, var/range, var/checkghosts = 1) - var/list/hear = dview(range,T,INVISIBILITY_MAXIMUM) - var/list/hearturfs = list() + var/list/mobs = list() + var/list/objs = list() - for(var/I in hear) - if(ismob(I)) - var/mob/M = I - results["mobs"] += M - hearturfs += M.locs[1] - else if(isobj(I)) - var/obj/O = I - results["objs"] += I - hearturfs += O.locs[1] + 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(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) - results["mobs"] |= M - continue - if(M.loc && M.locs[1] in hearturfs) - results["mobs"] |= M + 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 results + return list("mobs" = mobs, "objs" = objs) 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/_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 36304a13dc..e0c6c7a159 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -1,41 +1,60 @@ +/* +=== 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(!ismob(user)) + return 0 + if(can_operate(src) && do_surgery(src,user,I)) //Surgery + return 1 if(attempt_vr(src,"vore_attackby",args)) return //VOREStation Code - if(istype(I) && ismob(user)) - I.attack(src, user) - + 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 @@ -47,50 +66,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..aec056f7f6 100644 --- a/code/datums/supplypacks.dm +++ b/code/datums/supplypacks.dm @@ -1242,16 +1242,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..04e2c42622 --- /dev/null +++ b/code/datums/underwear/bottom.dm @@ -0,0 +1,145 @@ +/datum/category_item/underwear/bottom/none + name = "None" + always_last = TRUE + +/datum/category_item/underwear/bottom/briefs_white + name = "Briefs, white" + icon_state = "b1" + +/datum/category_item/underwear/bottom/briefs_white/is_default(var/gender) + return gender != FEMALE + +/datum/category_item/underwear/bottom/briefs_grey + name = "Briefs, grey" + icon_state = "b2" + +/datum/category_item/underwear/bottom/briefs_green + name = "Briefs, green" + icon_state = "b3" + +/datum/category_item/underwear/bottom/briefs_blue + name = "Briefs, blue" + icon_state = "b4" + +/datum/category_item/underwear/bottom/briefs_black + name = "Briefs, black" + icon_state = "b5" + +/datum/category_item/underwear/bottom/panties_noback + name = "Panties, noback" + icon_state = "b6" + +/datum/category_item/underwear/bottom/boxers_loveheart + name = "Boxers, Loveheart" + icon_state = "b7" + +/datum/category_item/underwear/bottom/boxers_black + name = "Boxers, black" + icon_state = "b8" + +/datum/category_item/underwear/bottom/boxers_grey + name = "Boxers, grey" + icon_state = "b9" + +/datum/category_item/underwear/bottom/boxers_green_and_blue + name = "Boxers, green & blue striped" + icon_state = "b10" + +/datum/category_item/underwear/bottom/panties_red + name = "Panties, red" + icon_state = "b11" + +/datum/category_item/underwear/bottom/panties_white + name = "Panties, white" + icon_state = "b12" + +/datum/category_item/underwear/bottom/panties_white/is_default(var/gender) + return gender == FEMALE + +/datum/category_item/underwear/bottom/panties_yellow + name = "Panties, yellow" + icon_state = "b13" + +/datum/category_item/underwear/bottom/panties_blue + name = "Panties, blue" + icon_state = "b14" + +/datum/category_item/underwear/bottom/panties_light_black + name = "Panties, light-black" + icon_state = "b15" + +/datum/category_item/underwear/bottom/thong + name = "Thong" + icon_state = "b16" + +/datum/category_item/underwear/bottom/panties_black + name = "Panties, black" + icon_state = "b17" + +/datum/category_item/underwear/bottom/panties_white_alt + name = "Bra, white alt" + icon_state = "b18" + +/datum/category_item/underwear/bottom/compression_shorts_black + name = "Compression shorts, black" + icon_state = "b19" + +/datum/category_item/underwear/bottom/compression_shorts_white + name = "Compression shorts, white" + icon_state = "b20" + +/datum/category_item/underwear/bottom/compression_shorts_baby_blue + name = "Compression shorts, baby-blue" + icon_state = "b21" + +/datum/category_item/underwear/bottom/panties_green + name = "Panties, green" + icon_state = "b22" + +/datum/category_item/underwear/bottom/compression_shorts_pink + name = "Compression shorts, pink" + icon_state = "b23" + +/datum/category_item/underwear/bottom/thong_violet + name = "Thong, violet" + icon_state = "b24" + +/datum/category_item/underwear/bottom/thong_alt + name = "Thong, alt" + icon_state = "b25" + +/datum/category_item/underwear/bottom/thong_violet_alt + name = "Thong, violet alt" + icon_state = "b26" + +/datum/category_item/underwear/bottom/alt_thong_black + name = "Alt. thong, black" + icon_state = "b27" + +/datum/category_item/underwear/bottom/alt_thong_blue + name = "Alt. thong, blue" + icon_state = "b28" + +/datum/category_item/underwear/bottom/alt_thong_green + name = "Alt. thong, green" + icon_state = "b29" + +/datum/category_item/underwear/bottom/alt_thong_purple + name = "Alt. thong, purple" + icon_state = "b30" + +/datum/category_item/underwear/bottom/alt_thong_red + name = "Alt. thong, red" + icon_state = "b31" + +/datum/category_item/underwear/bottom/alt_thong_teal + name = "Alt. thong, teal" + icon_state = "b32" + +/datum/category_item/underwear/bottom/alt_thong_violet + name = "Alt. thong, violet" + icon_state = "b33" + +/datum/category_item/underwear/bottom/alt_thong_white + name = "Alt. thong, white" + icon_state = "b34" \ 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..ab33162e04 --- /dev/null +++ b/code/datums/underwear/socks.dm @@ -0,0 +1,75 @@ +/datum/category_item/underwear/socks/none + always_last = TRUE + name = "None" + +/datum/category_item/underwear/socks/white_normal + name = "Normal, white" + icon_state = "white_norm" + +/datum/category_item/underwear/socks/white_short + name = "Short, white" + icon_state = "white_short" + +/datum/category_item/underwear/socks/white_knee + name = "Knee, white" + icon_state = "white_knee" + +/datum/category_item/underwear/socks/white_thigh + name = "Thigh, white" + icon_state = "white_thigh" + +/datum/category_item/underwear/socks/black_normal + name = "Normal, black" + icon_state = "black_norm" + +/datum/category_item/underwear/socks/black_short + name = "Short, black" + icon_state = "black_short" + +/datum/category_item/underwear/socks/black_knee + name = "Knee, black" + icon_state = "black_knee" + +/datum/category_item/underwear/socks/black_thigh + name = "Thigh, black" + icon_state = "black_thigh" + +/datum/category_item/underwear/socks/thin_knee + name = "Knee, thin" + icon_state = "thin_knee" + +/datum/category_item/underwear/socks/thin_thigh + name = "Thigh, thin" + icon_state = "thin_thigh" + +/datum/category_item/underwear/socks/pantyhose + name = "Pantyhose" + icon_state = "pantyhose" + +/datum/category_item/underwear/socks/striped_thigh + name = "Thigh, striped" + icon_state = "striped_thigh" + +/datum/category_item/underwear/socks/striped_knee + name = "Knee, striped" + icon_state = "striped_knee" + +/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" + +/datum/category_item/underwear/socks/thinwhite_thigh + name = "Thigh, thin white" + icon_state = "thinwhite_thigh" + +/datum/category_item/underwear/socks/thinwhite_knee + name = "Knee, thin white" + icon_state = "thinwhite_knee" \ 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..4e23a70295 --- /dev/null +++ b/code/datums/underwear/top.dm @@ -0,0 +1,106 @@ +/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_red + name = "Bra, red" + icon_state = "t1" + +/datum/category_item/underwear/top/bra_white + is_default = TRUE + name = "Bra, white" + icon_state = "t2" + +/datum/category_item/underwear/top/bra_white/is_default(var/gender) + return gender == FEMALE + +/datum/category_item/underwear/top/bra_yellow + name = "Bra, yellow" + icon_state = "t3" + +/datum/category_item/underwear/top/bra_blue + name = "Bra, blue" + icon_state = "t4" + +/datum/category_item/underwear/top/bra_black + name = "Bra, black" + icon_state = "t5" + +/datum/category_item/underwear/top/lacy_bra + name = "Lacy bra" + icon_state = "t6" + +/datum/category_item/underwear/top/sports_bra_black + name = "Sports bra, black" + icon_state = "t7" + +/datum/category_item/underwear/top/sports_bra_white + name = "Sports bra, white" + icon_state = "t8" + +/datum/category_item/underwear/top/sports_bra_black_alt + name = "Sports bra, black alt" + icon_state = "t9" + +/datum/category_item/underwear/top/sports_bra_white_alt + name = "Sports bra, white alt" + icon_state = "t10" + +/datum/category_item/underwear/top/bra_baby_blue + name = "Bra, baby blue" + icon_state = "t11" + +/datum/category_item/underwear/top/bra_green + name = "Bra, green" + icon_state = "t12" + +/datum/category_item/underwear/top/bra_pink + name = "Bra, pink" + icon_state = "t13" + +/datum/category_item/underwear/top/bra_violet + name = "Bra, violet" + icon_state = "t14" + +/datum/category_item/underwear/top/lacy_bra_alt + name = "Lacy bra, alt" + icon_state = "t15" + +/datum/category_item/underwear/top/bra_lacy_violet + name = "Lacy bra, violet" + icon_state = "t16" + +/datum/category_item/underwear/top/halterneck_bra_black + name = "Halterneck bra, black" + icon_state = "t17" + +/datum/category_item/underwear/top/halterneck_bra_blue + name = "Halterneck bra, blue" + icon_state = "t18" + +/datum/category_item/underwear/top/halterneck_bra_green + name = "Halterneck bra, green" + icon_state = "t19" + +/datum/category_item/underwear/top/halterneck_bra_purple + name = "Halterneck bra, purple" + icon_state = "t20" + +/datum/category_item/underwear/top/halterneck_bra_red + name = "Halterneck bra, red" + icon_state = "t21" + +/datum/category_item/underwear/top/halterneck_bra_teal + name = "Halterneck bra, teal" + icon_state = "t22" + +/datum/category_item/underwear/top/halterneck_bra_violet + name = "Halterneck bra, violet" + icon_state = "t23" + +/datum/category_item/underwear/top/halterneck_bra_white + name = "Halterneck bra, white" + icon_state = "t24" \ 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..f1149dabb7 --- /dev/null +++ b/code/datums/underwear/undershirts.dm @@ -0,0 +1,140 @@ +/datum/category_item/underwear/undershirt/none + is_default = TRUE + name = "None" + always_last = TRUE + +/datum/category_item/underwear/undershirt/tank_top_white + name = "Tank top, white" + icon_state = "u1" + +/datum/category_item/underwear/undershirt/tank_top_white_alt + name = "Tank top, white alt" + icon_state = "tank_white_s" + +/datum/category_item/underwear/undershirt/tank_top_black + name = "Tank top, black" + icon_state = "u2" + +/datum/category_item/underwear/undershirt/tank_top_red + name = "Tank top, red" + icon_state = "tank_red_s" + +/datum/category_item/underwear/undershirt/tank_top_fire + name = "Tank top, fire" + icon_state = "tank_fire_s" + +/datum/category_item/underwear/undershirt/shirt_black + name = "Shirt, black" + icon_state = "u3" + +/datum/category_item/underwear/undershirt/shirt_black_alt + name = "Shirt, black alt" + icon_state = "shirt_black_s" + +/datum/category_item/underwear/undershirt/shirt_white + name = "Shirt, white" + icon_state = "u4" + +/datum/category_item/underwear/undershirt/shirt_white_alt + name = "Shirt, white alt" + icon_state = "shirt_white_s" + +/datum/category_item/underwear/undershirt/shirt_grey + name = "Shirt, grey" + icon_state = "shirt_grey_s" + +/datum/category_item/underwear/undershirt/shirt_blue + name = "Shirt, blue" + icon_state = "blueshirt_s" + +/datum/category_item/underwear/undershirt/shirt_blue_alt + name = "Shirt, blue alt" + icon_state = "shirt_blue_s" + +/datum/category_item/underwear/undershirt/shirt_red + name = "Shirt, red" + icon_state = "redshirt_s" + +/datum/category_item/underwear/undershirt/shirt_red_alt + name = "Shirt, red alt" + icon_state = "shirt_red_s" + +/datum/category_item/underwear/undershirt/shirt_yellow + name = "Shirt, yellow" + icon_state = "yellowshirt_s" + +/datum/category_item/underwear/undershirt/shirt_green + name = "Shirt, green" + icon_state = "greenshirt_s" + +/datum/category_item/underwear/undershirt/shirt_green_alt + name = "Shirt, green alt" + icon_state = "shirt_green_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_white + name = "Shortsleeve shirt, white" + icon_state = "whiteshortsleeve_s" + +/datum/category_item/underwear/undershirt/shortsleeve_shirt_purple + name = "Shortsleeve shirt, purple" + icon_state = "purpleshortsleeve_s" + +/datum/category_item/underwear/undershirt/shortsleeve_shirt_blue + name = "Shortsleeve shirt, blue" + icon_state = "blueshortsleeve_s" + +/datum/category_item/underwear/undershirt/shortsleeve_shirt_green + name = "Shortsleeve shirt, green" + icon_state = "greenshortsleeve_s" + +/datum/category_item/underwear/undershirt/shortsleeve_shirt_black + name = "Shortsleeve shirt, black" + icon_state = "blackshortsleeve_s" + +/datum/category_item/underwear/undershirt/polo_shirt_blue + name = "Polo shirt, blue" + icon_state = "bluepolo_s" + +/datum/category_item/underwear/undershirt/polo_shirt_red + name = "Polo shirt, red" + icon_state = "redpolo_s" + +/datum/category_item/underwear/undershirt/polo_shirt_white + name = "Polo shirt, white" + icon_state = "whitepolo_s" + +/datum/category_item/underwear/undershirt/polo_shirt_grey_yellow + name = "Polo shirt, grey and yellow" + icon_state = "grayyellowpolo_s" + +/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..8b73dbddbb --- /dev/null +++ b/code/datums/underwear/underwear.dm @@ -0,0 +1,56 @@ +/**************************** +* 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 + +/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/apply_to_icon(var/icon/I) + if(!icon_state) + return + I.Blend(new /icon('icons/mob/human.dmi', icon_state), ICON_OVERLAY) \ 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/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/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 b1f6935175..6c575bef00 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 b8a873ff45..3d5806eeab 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -434,8 +434,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:" @@ -479,7 +480,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/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..a844d248e0 100644 --- a/code/game/machinery/kitchen/smartfridge.dm +++ b/code/game/machinery/kitchen/smartfridge.dm @@ -57,15 +57,21 @@ 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() + /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/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/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm index e2573a9661..4872e21ac7 100644 --- a/code/game/mecha/mech_fabricator.dm +++ b/code/game/mecha/mech_fabricator.dm @@ -28,7 +28,7 @@ /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) @@ -103,7 +103,7 @@ var/list/T = list() for(var/A in all_robolimbs) var/datum/robolimb/R = all_robolimbs[A] - if(!R.unavailable_to_produce) //VOREStation Edit + if(!R.unavailable_to_build) //VOREStation Edit T += list(list("id" = A, "company" = R.company)) data["manufacturers"] = T data["manufacturer"] = manufacturer 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 091b8cb2c9..6b5ad182c4 100644 --- a/code/game/objects/effects/decals/posters/bs12.dm +++ b/code/game/objects/effects/decals/posters/bs12.dm @@ -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/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/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/clown_items.dm b/code/game/objects/items/weapons/clown_items.dm index 7c8752c6d7..d15b78963d 100644 --- a/code/game/objects/items/weapons/clown_items.dm +++ b/code/game/objects/items/weapons/clown_items.dm @@ -53,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/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 index 180ccf2af9..94c266142e 100644 --- a/code/game/objects/items/weapons/towels.dm +++ b/code/game/objects/items/weapons/towels.dm @@ -6,7 +6,7 @@ force = 3.0 w_class = 3.0 attack_verb = list("whipped") - hitsound = "towelwhip" + hitsound = 'sound/weapons/towelwhip.ogg' desc = "A soft cotton towel." /obj/item/weapon/towel/attack_self(mob/living/user as mob) 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/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..2ded3ef17e 100644 --- a/code/game/objects/structures/under_wardrobe.dm +++ b/code/game/objects/structures/under_wardrobe.dm @@ -5,39 +5,51 @@ 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] (Remove)
" + + dat = jointext(dat) + H << browse(dat, "window=wardrobe;size=400x200") + +/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] + 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 + if(.) H.update_body(1) - - return 1 \ No newline at end of file + interact(H) \ No newline at end of file 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/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 7d5e08daf4..8548c00159 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -184,6 +184,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/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/preference_setup/general/01_basic.dm b/code/modules/client/preference_setup/general/01_basic.dm index c99d0d860a..d7b4bcddc5 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..b7758a6fa9 100644 --- a/code/modules/client/preference_setup/general/04_equipment.dm +++ b/code/modules/client/preference_setup/general/04_equipment.dm @@ -1,90 +1,75 @@ +/datum/preferences + var/list/all_underwear + /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["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["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 + + 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 -= UWC + else + var/datum/category_item/underwear/UWI = UWC.items_by_name[pref.all_underwear[underwear_category]] + if(!UWI) + pref.all_underwear -= UWC + 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]
" . += "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(.) /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 + + 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 216fc48896..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 * ********************/ 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_general.dm b/code/modules/client/preference_setup/loadout/loadout_general.dm index c68037704e..b659f8db13 100644 --- a/code/modules/client/preference_setup/loadout/loadout_general.dm +++ b/code/modules/client/preference_setup/loadout/loadout_general.dm @@ -68,4 +68,14 @@ /datum/gear/towel/New() ..() - gear_tweaks = list(gear_tweak_free_color_choice) \ No newline at end of file + 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 01209c7329..40bd1d767b 100644 --- a/code/modules/client/preference_setup/loadout/loadout_suit.dm +++ b/code/modules/client/preference_setup/loadout/loadout_suit.dm @@ -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/occupation/occupation.dm b/code/modules/client/preference_setup/occupation/occupation.dm index b74e7035b3..9cac3587d0 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/preference_setup/vore/01_ears.dm b/code/modules/client/preference_setup/vore/01_ears.dm index 3e3f559e61..6b783c1918 100644 --- a/code/modules/client/preference_setup/vore/01_ears.dm +++ b/code/modules/client/preference_setup/vore/01_ears.dm @@ -66,12 +66,13 @@ /datum/category_item/player_setup_item/vore/ears/content(var/mob/user) . += "

VORE Station Settings

" - 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") + . += "Preview
" - . += "
" + . += "
" + . += "
[pref.dress_mob ? "Hide equipment" : "Show equipment"]
" . += "Ears
" . += " Style: [pref.ear_style ? "Custom" : "Normal"]
" @@ -111,7 +112,7 @@ pref.ear_style = pretty_ear_styles[selection] else pref.ear_style = null - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["tail_style"]) // Construct the list of names allowed for this user. @@ -127,7 +128,7 @@ pref.tail_style = pretty_tail_styles[selection] else pref.tail_style = null - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["tail_color"]) var/new_tailc = input(user, "Choose your character's tail/taur colour:", "Character Preference", @@ -136,5 +137,10 @@ pref.r_tail = hex2num(copytext(new_tailc, 2, 4)) pref.g_tail = hex2num(copytext(new_tailc, 4, 6)) pref.b_tail = hex2num(copytext(new_tailc, 6, 8)) - 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/vore/02_size.dm b/code/modules/client/preference_setup/vore/02_size.dm index 7392b45c72..9a6e7fdf27 100644 --- a/code/modules/client/preference_setup/vore/02_size.dm +++ b/code/modules/client/preference_setup/vore/02_size.dm @@ -57,7 +57,7 @@ var/new_size = input(user, "Choose your character's size:", "Character Preference", pref.size_multiplier) as null|anything in size_types if(new_size) pref.size_multiplier = size_types[new_size] - return TOPIC_REFRESH + return TOPIC_REFRESH_UPDATE_PREVIEW else if(href_list["weight"]) var/new_weight = input(user, "Choose your character's relative body weight.\n\ diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index f99f77a59a..915a5db9ea 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,7 +248,7 @@ 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() @@ -262,6 +256,8 @@ datum/preferences player_setup.copy_to_mob(character) // VOREStation Edit - End + character.set_species(species) + if(be_random_name) real_name = random_name(identifying_gender,species) @@ -302,10 +298,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 @@ -351,10 +349,11 @@ 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() + for(var/underwear_category_name in all_underwear) + var/underwear_item_name = all_underwear[underwear_category_name] + var/datum/category_group/underwear/underwear_category = global_underwear.categories_by_name[underwear_category_name] + character.all_underwear[underwear_category_name] = underwear_category.items_by_name[underwear_item_name] if(backbag > 4 || backbag < 1) backbag = 1 //Same as above @@ -364,7 +363,12 @@ 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_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 dd16651136..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 @@ -748,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/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/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..d4ab2e4d0f 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,26 @@ 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/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 +124,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/detectivework/tools/rag.dm b/code/modules/detectivework/tools/rag.dm index b279ab63d5..06ee34b312 100644 --- a/code/modules/detectivework/tools/rag.dm +++ b/code/modules/detectivework/tools/rag.dm @@ -156,10 +156,16 @@ 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/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..6f4f82b8b8 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -400,7 +400,7 @@ "potassium", "plasticide", "mutationtoxin", - "amutationtoxin", + "docilitytoxin", "inaprovaline", "space_drugs", "paroxetine", 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/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/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/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/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/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index ecde71425f..73b7467e7e 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,12 +82,10 @@ msg += "[src.name]" - var/is_synth = isSynthetic() if(!(skipjumpsuit && skipface)) if(src.custom_species) msg += ", a [src.custom_species]" - - else if(is_synth) + else if(looks_synth) var/use_gender = "a synthetic" if(gender == MALE) use_gender = "an android" @@ -127,8 +168,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)) @@ -287,15 +326,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 370a63c8e4..728de86748 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -315,23 +315,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 01351dcf22..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) 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 e4a17174f8..036d2094ea 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -33,11 +33,9 @@ var/age = 30 //Player's age (pure fluff) var/b_type = "A+" //Player's bloodtype + var/datum/robolimb/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/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. @@ -85,7 +83,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..db0f44ce31 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -58,6 +58,37 @@ /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) + 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_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 117292ebaa..d61ab57048 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -1512,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)) 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/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index e8edb8aae9..aee262477c 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -220,6 +220,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 +249,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 @@ -316,16 +319,10 @@ var/global/list/damage_icon_parts = list() 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(species.appearance_flags & HAS_UNDERWEAR) + for(var/category in all_underwear) + var/datum/category_item/underwear/UW = all_underwear[category] + UW.apply_to_icon(stand_icon) if(update_icons) update_icons() @@ -783,6 +780,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/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/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/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/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/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 10908e074b..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 = view(src, dist) //hearers(src, dist) <- This shit was broken and bad and caused a lot of lag because mobs would chase targets they can't see. -Spades - - 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/hostile/vore/bear.dm b/code/modules/mob/living/simple_animal/hostile/vore/bear.dm index d843351d69..38ec88c1be 100644 --- a/code/modules/mob/living/simple_animal/hostile/vore/bear.dm +++ b/code/modules/mob/living/simple_animal/hostile/vore/bear.dm @@ -65,16 +65,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)) @@ -92,14 +92,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 @@ -107,15 +107,15 @@ /mob/living/simple_animal/hostile/vore/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/vore/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 ..() @@ -127,7 +127,7 @@ . = ..() if(.) custom_emote(1,"stares alertly at [.]") - stance = HOSTILE_STANCE_ALERT + stance = STANCE_ALERT /mob/living/simple_animal/hostile/vore/bear/LoseTarget() ..(5) @@ -167,4 +167,4 @@ else if(isliving(target_mob)) var/mob/living/L = target_mob L.adjustBruteLoss(damage) - return L \ No newline at end of file + return L diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 8742ebe510..389f905b38 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 @@ -205,6 +232,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() @@ -288,28 +332,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 @@ -403,3 +447,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 4cc88a9d14..e197939d1b 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) @@ -424,7 +428,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 a3f53372d7..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,599 +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)) - //VOREStation Code Start - if(current_species.color_mult) - limb_icon.Blend(rgb(r_skin, g_skin, b_skin), ICON_MULTIPLY) - else - limb_icon.Blend(rgb(r_skin, g_skin, b_skin), ICON_ADD) - //VOREstation Code End - // 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) - - //Tails - - // VoreStation Edit - Start - var/show_species_tail = 1 - if(src.tail_style && tail_styles_list[src.tail_style]) - var/datum/sprite_accessory/tail/tail_meta = tail_styles_list[src.tail_style] - var/icon/tail_s = new/icon("icon" = tail_meta.icon, "icon_state" = tail_meta.icon_state) - if(tail_meta.do_colouration) - tail_s.Blend(rgb(src.r_tail, src.g_tail, src.b_tail), tail_meta.color_blend_mode) - if(tail_meta.extra_overlay) - var/icon/overlay = new/icon("icon" = tail_meta.icon, "icon_state" = tail_meta.extra_overlay) - tail_s.Blend(overlay, ICON_OVERLAY) - qdel(overlay) - - show_species_tail = tail_meta.show_species_tail - if(istype(tail_meta, /datum/sprite_accessory/tail/taur)) - preview_icon.Blend(tail_s, ICON_OVERLAY, -16) - else - preview_icon.Blend(tail_s, ICON_OVERLAY) - // VoreStation Edit - End - - if(show_species_tail && current_species && (current_species.tail)) - var/icon/temp = new/icon( - "icon" = (current_species.icobase_tail ? current_species.icobase : 'icons/effects/species.dmi'), - "icon_state" = "[current_species.tail]_s") - if(current_species && (current_species.appearance_flags & HAS_SKIN_COLOR)) - //VOREStation Code Start - if(current_species.color_mult) - temp.Blend(rgb(r_skin, g_skin, b_skin), ICON_MULTIPLY) - else - temp.Blend(rgb(r_skin, g_skin, b_skin), ICON_ADD) - //VOREStation Code End - 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) - - // VoreStation Edit - Start - // Ear Items - var/datum/sprite_accessory/ears/ears_meta = ear_styles_list[src.ear_style] - if(ears_meta) - var/icon/ears_s = icon("icon" = ears_meta.icon, "icon_state" = ears_meta.icon_state) - if(ears_meta.do_colouration) - ears_s.Blend(rgb(src.r_hair, src.g_hair, src.b_hair), ears_meta.color_blend_mode) - if(ears_meta.extra_overlay) - var/icon/overlay = new/icon("icon" = ears_meta.icon, "icon_state" = ears_meta.extra_overlay) - ears_s.Blend(overlay, ICON_OVERLAY) - eyes_s.Blend(ears_s, ICON_OVERLAY) - // Vore Station Edit - End - - 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/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..5c096aa9d6 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. @@ -77,7 +78,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 +97,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 +232,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 +244,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 +318,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 +344,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..419d82ea64 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -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 diff --git a/code/modules/organs/organ_icon.dm b/code/modules/organs/organ_icon.dm index 6a698fc349..439de74939 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..6738b2b20a 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,120 @@ 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' -/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' + 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' + 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' -/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' + 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' + 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' + monitor_styles = standard_monitor_styles + +/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' + parts = list(BP_HEAD) + unavailable_at_chargen = null + +/datum/robolimb/veymed + lifelike = 1 + blood_color = "#CCCCCC" + company = "Vey-Med" + desc = "This high quality limb is nearly indistinguishable from an organic one." + icon = 'icons/mob/human_races/cyberlimbs/veymed/veymed_main.dmi' /datum/robolimb/wardtakahashi company = "Ward-Takahashi" desc = "This limb features sleek black and white polymers." - icon = 'icons/mob/human_races/cyberlimbs/wardtakahashi.dmi' + icon = 'icons/mob/human_races/cyberlimbs/wardtakahashi/wardtakahashi_main.dmi' -/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 +/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' + 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' + 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' + +/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' + 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' + 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' diff --git a/code/modules/organs/robolimbs_vr.dm b/code/modules/organs/robolimbs_vr.dm index 032db60bd8..027e8e0aed 100644 --- a/code/modules/organs/robolimbs_vr.dm +++ b/code/modules/organs/robolimbs_vr.dm @@ -1,5 +1,4 @@ /datum/robolimb - var/unavailable_to_produce //Makes robolimbs not listed in exosuit fabs. AKA you have to varedit them on or spawn them. var/includes_tail //Cyberlimbs dmi includes a tail sprite to wear. /datum/robolimb/kitsuhana @@ -7,5 +6,5 @@ desc = "This limb seems rather vulpine and fuzzy, with realistic-feeling flesh." icon = 'icons/mob/human_races/cyberlimbs/kitsuhana.dmi' includes_tail = 1 - unavailable_to_produce = 1 + unavailable_to_build = 1 unavailable_at_chargen = 1 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/power/cable.dm b/code/modules/power/cable.dm index 51ffc6f7b6..e4bbbec5f6 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -522,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) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index ab052834ec..92066ae899 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -221,7 +221,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 @@ -426,4 +426,4 @@ return new_mode /obj/item/weapon/gun/attack_self(mob/user) - switch_firemodes(user) \ No newline at end of file + switch_firemodes(user) 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/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-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 3b78dc7326..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. diff --git a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm index 712772a54b..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) @@ -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/snacks.dm b/code/modules/reagents/reagent_containers/food/snacks.dm index 854e6b7281..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)]") 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 9e22362872..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 diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 3c51df071e..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 diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index 05b8a82f2f..92ba843274 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -756,6 +756,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" 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..cc4143658c 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 diff --git a/code/modules/vore/appearance/update_icons_vr.dm b/code/modules/vore/appearance/update_icons_vr.dm index 381b045615..0b34738c13 100644 --- a/code/modules/vore/appearance/update_icons_vr.dm +++ b/code/modules/vore/appearance/update_icons_vr.dm @@ -15,12 +15,9 @@ /mob/living/carbon/human/proc/get_tail_image() //If you are FBP with tail style - if(full_prosthetic && ("groin" in organs_by_name) && organs_by_name["groin"]) - var/obj/item/organ/external/groin/G = organs_by_name["groin"] - var/datum/robolimb/R = all_robolimbs[G.model] - if(R.includes_tail) - var/icon/tail_s = new/icon("icon" = R.icon, "icon_state" = "tail") - return image(tail_s) + if(synthetic && synthetic.includes_tail) + var/icon/tail_s = new/icon("icon" = synthetic.icon, "icon_state" = "tail") + return image(tail_s) //If you have a custom tail selected if(tail_style && !(wear_suit && wear_suit.flags_inv & HIDETAIL && !isTaurTail(tail_style))) 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..8ef1efb5cc --- /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 = 200 + mutable = COLORMUT + var/is_child = 1 + var/cores = 3 + var/growthcounter = 0 + var/growthpoint = 150 //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..b98bf334d6 --- /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/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 82e7175c06..7199a0ef38 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -53,6 +53,26 @@ -->
+

29 May 2016

+

Datraen updated:

+ +

Zuhayr updated:

+ + +

27 May 2016

+

Yoshax updated:

+ +

25 May 2016

Serithi updated: