diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index 74b68d6b05b..4d6d82147f2 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -12,3 +12,4 @@ var/global/list/mob_list = list() //all mobs, including clientless var/global/list/living_mob_list = list() //all alive mobs, including clientless. Excludes /mob/new_player var/global/list/dead_mob_list = list() //all dead mobs, including clientless. Excludes /mob/new_player var/global/list/joined_player_list = list() //all clients that have joined the game at round-start or as a latejoin. +var/global/list/silicon_mobs = list() //all silicon mobs \ No newline at end of file diff --git a/code/game/alternate_appearance.dm b/code/game/alternate_appearance.dm new file mode 100644 index 00000000000..005876e8ccc --- /dev/null +++ b/code/game/alternate_appearance.dm @@ -0,0 +1,155 @@ +/* + Alternate Appearances! By RemieRichards + A framework for replacing an atom (and it's overlays) with an override = 1 image, that's less shit! + Example uses: + * hallucinating all mobs looking like skeletons + * people wearing cardborg suits appearing as Standard Cyborgs to the other Silicons + * !!use your imagination!! + +*/ + + +//This datum is built on-the-fly by some of the procs below +//no need to instantiate it +/datum/alternate_appearance + var/key = "" + var/image/img + var/list/viewers = list() + var/atom/owner = null + + +/* + Displays the alternate_appearance + displayTo - a list of MOBS to show this appearance to +*/ +/datum/alternate_appearance/proc/display_to(list/displayTo) + if(!displayTo || !displayTo.len) + return + for(var/m in displayTo) + var/mob/M = m + if(!M.viewing_alternate_appearances) + M.viewing_alternate_appearances = list() + viewers |= M + M.viewing_alternate_appearances[key] = src + if(M.client) + M.client.images |= img + +/* + Hides the alternate_appearance + hideFrom - optional list of MOBS to hide it from the list's mobs specifically +*/ +/datum/alternate_appearance/proc/hide(list/hideFrom) + var/list/hiding = viewers + if(hideFrom) + hiding = hideFrom + + for(var/m in hiding) + var/mob/M = m + if(M.client) + M.client.images -= img + if(M.viewing_alternate_appearances && M.viewing_alternate_appearances.len) + M.viewing_alternate_appearances -= key + if(!M.viewing_alternate_appearances.len) + M.viewing_alternate_appearances = null + viewers -= M + + +/* + Removes the alternate_appearance from its owner's alternate_appearances list, hiding it also +*/ +/datum/alternate_appearance/proc/remove() + hide() + if(owner && owner.alternate_appearances) + owner.alternate_appearances -= key + if(!owner.alternate_appearances.len) + owner.alternate_appearances = null + + +/datum/alternate_appearance/Destroy() + remove() + return ..() + + + +/atom + var/list/alternate_appearances //the alternate appearances we own + var/list/viewing_alternate_appearances //the alternate appearances we're viewing, stored here to reestablish them after Logout()s + //these lists are built/destroyed as necessary, so atoms aren't all lugging around lists full of datums + +/* + Builds an alternate_appearance datum for the supplied args, optionally displaying it straight away + key - the key to the assoc list of key = /datum/alternate_appearances + img - the image file to be the "alternate appearance" + WORKS BEST IF: + * it has override = 1 set + * the image's loc is the atom that will use the appearance (otherwise... it's not exactly an alt appearance of this atom is it?) + displayTo - optional list of MOBS to display to immediately + + Example: + var/image/I = image(icon = 'disguise.dmi', icon_state = "disguise", loc = src) + I.override = 1 + add_alt_appearance("super_secret_disguise", I, players) + +*/ +/atom/proc/add_alt_appearance(key, img, list/displayTo = list()) + if(!key || !img) + return + if(!alternate_appearances) + alternate_appearances = list() + + var/datum/alternate_appearance/AA = new() + AA.img = img + AA.key = key + AA.owner = src + + alternate_appearances[key] = AA + if(displayTo && displayTo.len) + display_alt_appearance(key, displayTo) + + +////////////// +// WRAPPERS // +////////////// + +/* + Removes an alternate_appearance from src's alternate_appearances list + Wrapper for: alternate_appearance/remove() + key - the key to the assoc list of key = /datum/alternate_appearance +*/ +/atom/proc/remove_alt_appearance(key) + if(alternate_appearances) + if(alternate_appearances[key]) + var/datum/alternate_appearance/AA = alternate_appearances[key] + qdel(AA) + + +/* + Displays an alternate appearance from src's alternate_appearances list + Wrapper for: alternate_appearance/display_to() + key - the key to the assoc list of key = /datum/alternate_appearance + displayTo - a list of MOBS to show this appearance to +*/ +/atom/proc/display_alt_appearance(key, list/displayTo) + if(!alternate_appearances || !key) + return + var/datum/alternate_appearance/AA = alternate_appearances[key] + if(!AA || !AA.img) + return + AA.display_to(displayTo) + + +/* + Hides an alternate appearance from src's alternate_appearances list + Wrapper for: alternate_appearance/hide() + key - the key to the assoc list of key = /datum/alternate_appearance + hideFrom - optional list of MOBS to hide it from the list's mobs specifically +*/ +/atom/proc/hide_alt_appearance(key, list/hideFrom) + if(!alternate_appearances || !key) + return + var/datum/alternate_appearance/AA = alternate_appearances[key] + if(!AA) + return + AA.hide(hideFrom) + + diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 2a78972e001..6dd095996f9 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1,393 +1,404 @@ -/atom - layer = 2 - var/level = 2 - var/flags = 0 - var/list/fingerprints - var/list/fingerprintshidden - var/fingerprintslast = null - var/list/blood_DNA - - ///Chemistry. - var/datum/reagents/reagents = null - - //This atom's HUD (med/sec, etc) images. Associative list. - var/list/image/hud_list = list() - //HUD images that this atom can provide. - var/list/hud_possible - - //Value used to increment ex_act() if reactionary_explosions is on - var/explosion_block = 0 - -/atom/proc/onCentcom() - var/turf/T = get_turf(src) - if(!T) - return 0 - - if(T.z != ZLEVEL_CENTCOM)//if not, don't bother - return 0 - - //check for centcomm shuttles - for(var/A in SSshuttle.mobile) - var/obj/docking_port/mobile/M = A - if(M.launch_status == ENDGAME_LAUNCHED && T in M.areaInstance) - return 1 - - //finally check for centcom itself - return istype(T.loc,/area/centcom) - -/atom/proc/onSyndieBase() - var/turf/T = get_turf(src) - if(!T) - return 0 - - if(T.z != ZLEVEL_CENTCOM)//if not, don't bother - return 0 - - if(istype(T.loc,/area/shuttle/syndicate) || istype(T.loc,/area/syndicate_mothership)) - return 1 - - return 0 - -/atom/proc/attack_hulk(mob/living/carbon/human/hulk, do_attack_animation = 0) - if(do_attack_animation) - hulk.changeNext_move(CLICK_CD_MELEE) - add_logs(hulk, src, "punched", "hulk powers") - hulk.do_attack_animation(src) - return - -/atom/proc/CheckParts() - return - -/atom/proc/assume_air(datum/gas_mixture/giver) - qdel(giver) - return null - -/atom/proc/remove_air(amount) - return null - -/atom/proc/return_air() - if(loc) - return loc.return_air() - else - return null - -/atom/proc/check_eye(mob/user) - return - -/atom/proc/on_reagent_change() - return - -/atom/proc/Bumped(AM as mob|obj) - return - -// Convenience proc to see if a container is open for chemistry handling -// returns true if open -// false if closed -/atom/proc/is_open_container() - return flags & OPENCONTAINER - -/*//Convenience proc to see whether a container can be accessed in a certain way. - -/atom/proc/can_subract_container() - return flags & EXTRACT_CONTAINER - -/atom/proc/can_add_container() - return flags & INSERT_CONTAINER -*/ - - -/atom/proc/allow_drop() - return 1 - -/atom/proc/CheckExit() - return 1 - -/atom/proc/HasProximity(atom/movable/AM as mob|obj) - return - -/atom/proc/emp_act(severity) - return - -/atom/proc/bullet_act(obj/item/projectile/P, def_zone) - . = P.on_hit(src, 0, def_zone) - -/atom/proc/in_contents_of(container)//can take class or object instance as argument - if(ispath(container)) - if(istype(src.loc, container)) - return 1 - else if(src in container) - return 1 - return - -/* - * atom/proc/search_contents_for(path,list/filter_path=null) - * Recursevly searches all atom contens (including contents contents and so on). - * - * ARGS: path - search atom contents for atoms of this type - * list/filter_path - if set, contents of atoms not of types in this list are excluded from search. - * - * RETURNS: list of found atoms - */ - -/atom/proc/search_contents_for(path,list/filter_path=null) - var/list/found = list() - for(var/atom/A in src) - if(istype(A, path)) - found += A - if(filter_path) - var/pass = 0 - for(var/type in filter_path) - pass |= istype(A, type) - if(!pass) - continue - if(A.contents.len) - found += A.search_contents_for(path,filter_path) - return found - - -/atom/proc/examine(mob/user) - //This reformat names to get a/an properly working on item descriptions when they are bloody - var/f_name = "\a [src]." - if(src.blood_DNA && !istype(src, /obj/effect/decal)) - if(gender == PLURAL) - f_name = "some " - else - f_name = "a " - f_name += "blood-stained [name]!" - - user << "\icon[src] That's [f_name]" - - if(desc) - user << desc - // *****RM - //user << "[name]: Dn:[density] dir:[dir] cont:[contents] icon:[icon] is:[icon_state] loc:[loc]" - - if(reagents && is_open_container()) //is_open_container() isn't really the right proc for this, but w/e - user << "It contains:" - if(reagents.reagent_list.len) - if(user.can_see_reagents()) //Show each individual reagent - for(var/datum/reagent/R in reagents.reagent_list) - user << "[R.volume] units of [R.name]" - else //Otherwise, just show the total volume - var/total_volume = 0 - for(var/datum/reagent/R in reagents.reagent_list) - total_volume += R.volume - user << "[total_volume] units of various reagents" - else - user << "Nothing." - -/atom/proc/relaymove() - return - -/atom/proc/contents_explosion(severity, target) - for(var/atom/A in contents) - A.ex_act(severity, target) - CHECK_TICK - -/atom/proc/ex_act(severity, target) - contents_explosion(severity, target) - -/atom/proc/blob_act() - return - -/atom/proc/fire_act() - return - -/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked) - if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). - spawn(2) //very short wait, so we can actually see the impact. - if(AM && isturf(AM.loc)) - step(AM, turn(AM.dir, 180)) - -var/list/blood_splatter_icons = list() - -/atom/proc/blood_splatter_index() - return "\ref[initial(icon)]-[initial(icon_state)]" - -/atom/proc/add_blood_list(mob/living/carbon/M) - // Returns 0 if we have that blood already - if(!istype(blood_DNA, /list)) //if our list of DNA doesn't exist yet (or isn't a list) initialise it. - blood_DNA = list() - //if this blood isn't already in the list, add it - if(blood_DNA[M.dna.unique_enzymes]) - return 0 //already bloodied with this blood. Cannot add more. - blood_DNA[M.dna.unique_enzymes] = M.dna.blood_type - return 1 - -//returns 1 if made bloody, returns 0 otherwise -/atom/proc/add_blood(mob/living/carbon/M) - if(!M || !M.has_dna() || rejects_blood()) - return 0 - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(NOBLOOD in H.dna.species.specflags) - return 0 - return 1 - -/obj/add_blood(mob/living/carbon/M) - if(!..()) - return 0 - return add_blood_list(M) - -/obj/item/add_blood(mob/living/carbon/M) - var/blood_count = !blood_DNA ? 0 : blood_DNA.len - if(!..()) - return 0 - //apply the blood-splatter overlay if it isn't already in there - if(!blood_count && initial(icon) && initial(icon_state)) - //try to find a pre-processed blood-splatter. otherwise, make a new one - var/index = blood_splatter_index() - var/icon/blood_splatter_icon = blood_splatter_icons[index] - if(!blood_splatter_icon) - blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object - blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) - blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant - blood_splatter_icon = fcopy_rsc(blood_splatter_icon) - blood_splatter_icons[index] = blood_splatter_icon - overlays += blood_splatter_icon - return 1 //we applied blood to the item - -/obj/item/clothing/gloves/add_blood(mob/living/carbon/M) - if(!..()) - return 0 - transfer_blood = rand(2, 4) - bloody_hands_mob = M - return 1 - -/turf/add_blood(mob/living/carbon/human/M) - if(!..()) - return 0 - - var/obj/effect/decal/cleanable/blood/B = locate() in contents //check for existing blood splatter - if(!B) - blood_splatter(src,M.get_blood(M.vessel),1) - B = locate(/obj/effect/decal/cleanable/blood) in contents - B.add_blood_list(M) - return 1 //we bloodied the floor - -/mob/living/carbon/human/add_blood(mob/living/carbon/M) - if(!..()) - return 0 - add_blood_list(M) - bloody_hands = rand(2, 4) - bloody_hands_mob = M - update_inv_gloves() //handles bloody hands overlays and updating - return 1 //we applied blood to the item - -/atom/proc/rejects_blood() - return 0 - -// Only adds blood on the floor -- Skie -/atom/proc/add_blood_floor(mob/living/carbon/M) - if(istype(src, /turf)) - if(M.has_dna()) //mobs with dna = (monkeys + humans at time of writing) - var/obj/effect/decal/cleanable/blood/B = locate() in contents - if(!B) - blood_splatter(src,M,1) - B = locate(/obj/effect/decal/cleanable/blood) in contents - B.blood_DNA[M.dna.unique_enzymes] = M.dna.blood_type - else if(istype(M, /mob/living/carbon/alien)) - var/obj/effect/decal/cleanable/xenoblood/B = locate() in contents - if(!B) - B = new(src) - B.blood_DNA["UNKNOWN BLOOD"] = "X*" - else if(istype(M, /mob/living/silicon/robot)) - var/obj/effect/decal/cleanable/oil/B = locate() in contents - if(!B) - B = new(src) - -/atom/proc/clean_blood() - if(istype(blood_DNA, /list)) - blood_DNA = null - return 1 - - -/atom/proc/get_global_map_pos() - if(!islist(global_map) || isemptylist(global_map)) return - var/cur_x = null - var/cur_y = null - var/list/y_arr = null - for(cur_x=1,cur_x<=global_map.len,cur_x++) - y_arr = global_map[cur_x] - cur_y = y_arr.Find(src.z) - if(cur_y) - break -// world << "X = [cur_x]; Y = [cur_y]" - if(cur_x && cur_y) - return list("x"=cur_x,"y"=cur_y) - else - return 0 - -/atom/proc/isinspace() - if(istype(get_turf(src), /turf/open/space)) - return 1 - else - return 0 - -/atom/proc/handle_fall() - return - -/atom/proc/handle_slip() - return -/atom/proc/singularity_act() - return - -/atom/proc/singularity_pull() - return - -/atom/proc/acid_act(acidpwr, toxpwr, acid_volume) - return - -/atom/proc/emag_act() - return - -/atom/proc/narsie_act() - return - -/atom/proc/storage_contents_dump_act(obj/item/weapon/storage/src_object, mob/user) - return 0 - -//This proc is called on the location of an atom when the atom is Destroy()'d -/atom/proc/handle_atom_del(atom/A) - -// Byond seemingly calls stat, each tick. -// Calling things each tick can get expensive real quick. -// So we slow this down a little. -// See: http://www.byond.com/docs/ref/info.html#/client/proc/Stat -/atom/Stat() - . = ..() - sleep(1) - stoplag() - -//This is called just before maps and objects are initialized, use it to spawn other mobs/objects -//effects at world start up without causing runtimes -/atom/proc/spawn_atom_to_world() - -//This will be called after the map and objects are loaded -/atom/proc/initialize() - return - -//the vision impairment to give to the mob whose perspective is set to that atom (e.g. an unfocused camera giving you an impaired vision when looking through it) -/atom/proc/get_remote_view_fullscreens(mob/user) - return - -//the sight changes to give to the mob whose perspective is set to that atom (e.g. A mob with nightvision loses its nightvision while looking through a normal camera) -/atom/proc/update_remote_sight(mob/living/user) - return - -/atom/proc/add_vomit_floor(mob/living/carbon/M, toxvomit = 0) - if(istype(src,/turf) ) - var/obj/effect/decal/cleanable/vomit/V = PoolOrNew(/obj/effect/decal/cleanable/vomit, src) - // Make toxins vomit look different - if(toxvomit) - V.icon_state = "vomittox_[pick(1,4)]" - if(M.reagents) - clear_reagents_to_vomit_pool(M,V) - -/atom/proc/clear_reagents_to_vomit_pool(mob/living/carbon/M, obj/effect/decal/cleanable/vomit/V) - M.reagents.trans_to(V, M.reagents.total_volume / 10) - for(var/datum/reagent/R in M.reagents.reagent_list) //clears the stomach of anything that might be digested as food - if(istype(R, /datum/reagent/consumable)) - var/datum/reagent/consumable/nutri_check = R - if(nutri_check.nutriment_factor >0) - M.reagents.remove_reagent(R.id,R.volume) +/atom + layer = 2 + var/level = 2 + var/flags = 0 + var/list/fingerprints + var/list/fingerprintshidden + var/fingerprintslast = null + var/list/blood_DNA + + ///Chemistry. + var/datum/reagents/reagents = null + + //This atom's HUD (med/sec, etc) images. Associative list. + var/list/image/hud_list = list() + //HUD images that this atom can provide. + var/list/hud_possible + + //Value used to increment ex_act() if reactionary_explosions is on + var/explosion_block = 0 + + +/atom/Destroy() + if(alternate_appearances) + for(var/aakey in alternate_appearances) + var/datum/alternate_appearance/AA = alternate_appearances[aakey] + qdel(AA) + alternate_appearances = null + + return ..() + + +/atom/proc/onCentcom() + var/turf/T = get_turf(src) + if(!T) + return 0 + + if(T.z != ZLEVEL_CENTCOM)//if not, don't bother + return 0 + + //check for centcomm shuttles + for(var/A in SSshuttle.mobile) + var/obj/docking_port/mobile/M = A + if(M.launch_status == ENDGAME_LAUNCHED && T in M.areaInstance) + return 1 + + //finally check for centcom itself + return istype(T.loc,/area/centcom) + +/atom/proc/onSyndieBase() + var/turf/T = get_turf(src) + if(!T) + return 0 + + if(T.z != ZLEVEL_CENTCOM)//if not, don't bother + return 0 + + if(istype(T.loc,/area/shuttle/syndicate) || istype(T.loc,/area/syndicate_mothership)) + return 1 + + return 0 + +/atom/proc/attack_hulk(mob/living/carbon/human/hulk, do_attack_animation = 0) + if(do_attack_animation) + hulk.changeNext_move(CLICK_CD_MELEE) + add_logs(hulk, src, "punched", "hulk powers") + hulk.do_attack_animation(src) + return + +/atom/proc/CheckParts() + return + +/atom/proc/assume_air(datum/gas_mixture/giver) + qdel(giver) + return null + +/atom/proc/remove_air(amount) + return null + +/atom/proc/return_air() + if(loc) + return loc.return_air() + else + return null + +/atom/proc/check_eye(mob/user) + return + +/atom/proc/on_reagent_change() + return + +/atom/proc/Bumped(AM as mob|obj) + return + +// Convenience proc to see if a container is open for chemistry handling +// returns true if open +// false if closed +/atom/proc/is_open_container() + return flags & OPENCONTAINER + +/*//Convenience proc to see whether a container can be accessed in a certain way. + +/atom/proc/can_subract_container() + return flags & EXTRACT_CONTAINER + +/atom/proc/can_add_container() + return flags & INSERT_CONTAINER +*/ + + +/atom/proc/allow_drop() + return 1 + +/atom/proc/CheckExit() + return 1 + +/atom/proc/HasProximity(atom/movable/AM as mob|obj) + return + +/atom/proc/emp_act(severity) + return + +/atom/proc/bullet_act(obj/item/projectile/P, def_zone) + . = P.on_hit(src, 0, def_zone) + +/atom/proc/in_contents_of(container)//can take class or object instance as argument + if(ispath(container)) + if(istype(src.loc, container)) + return 1 + else if(src in container) + return 1 + return + +/* + * atom/proc/search_contents_for(path,list/filter_path=null) + * Recursevly searches all atom contens (including contents contents and so on). + * + * ARGS: path - search atom contents for atoms of this type + * list/filter_path - if set, contents of atoms not of types in this list are excluded from search. + * + * RETURNS: list of found atoms + */ + +/atom/proc/search_contents_for(path,list/filter_path=null) + var/list/found = list() + for(var/atom/A in src) + if(istype(A, path)) + found += A + if(filter_path) + var/pass = 0 + for(var/type in filter_path) + pass |= istype(A, type) + if(!pass) + continue + if(A.contents.len) + found += A.search_contents_for(path,filter_path) + return found + + +/atom/proc/examine(mob/user) + //This reformat names to get a/an properly working on item descriptions when they are bloody + var/f_name = "\a [src]." + if(src.blood_DNA && !istype(src, /obj/effect/decal)) + if(gender == PLURAL) + f_name = "some " + else + f_name = "a " + f_name += "blood-stained [name]!" + + user << "\icon[src] That's [f_name]" + + if(desc) + user << desc + // *****RM + //user << "[name]: Dn:[density] dir:[dir] cont:[contents] icon:[icon] is:[icon_state] loc:[loc]" + + if(reagents && is_open_container()) //is_open_container() isn't really the right proc for this, but w/e + user << "It contains:" + if(reagents.reagent_list.len) + if(user.can_see_reagents()) //Show each individual reagent + for(var/datum/reagent/R in reagents.reagent_list) + user << "[R.volume] units of [R.name]" + else //Otherwise, just show the total volume + var/total_volume = 0 + for(var/datum/reagent/R in reagents.reagent_list) + total_volume += R.volume + user << "[total_volume] units of various reagents" + else + user << "Nothing." + +/atom/proc/relaymove() + return + +/atom/proc/contents_explosion(severity, target) + for(var/atom/A in contents) + A.ex_act(severity, target) + CHECK_TICK + +/atom/proc/ex_act(severity, target) + contents_explosion(severity, target) + +/atom/proc/blob_act() + return + +/atom/proc/fire_act() + return + +/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked) + if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). + spawn(2) //very short wait, so we can actually see the impact. + if(AM && isturf(AM.loc)) + step(AM, turn(AM.dir, 180)) + +var/list/blood_splatter_icons = list() + +/atom/proc/blood_splatter_index() + return "\ref[initial(icon)]-[initial(icon_state)]" + +/atom/proc/add_blood_list(mob/living/carbon/M) + // Returns 0 if we have that blood already + if(!istype(blood_DNA, /list)) //if our list of DNA doesn't exist yet (or isn't a list) initialise it. + blood_DNA = list() + //if this blood isn't already in the list, add it + if(blood_DNA[M.dna.unique_enzymes]) + return 0 //already bloodied with this blood. Cannot add more. + blood_DNA[M.dna.unique_enzymes] = M.dna.blood_type + return 1 + +//returns 1 if made bloody, returns 0 otherwise +/atom/proc/add_blood(mob/living/carbon/M) + if(!M || !M.has_dna() || rejects_blood()) + return 0 + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(NOBLOOD in H.dna.species.specflags) + return 0 + return 1 + +/obj/add_blood(mob/living/carbon/M) + if(!..()) + return 0 + return add_blood_list(M) + +/obj/item/add_blood(mob/living/carbon/M) + var/blood_count = !blood_DNA ? 0 : blood_DNA.len + if(!..()) + return 0 + //apply the blood-splatter overlay if it isn't already in there + if(!blood_count && initial(icon) && initial(icon_state)) + //try to find a pre-processed blood-splatter. otherwise, make a new one + var/index = blood_splatter_index() + var/icon/blood_splatter_icon = blood_splatter_icons[index] + if(!blood_splatter_icon) + blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object + blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) + blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant + blood_splatter_icon = fcopy_rsc(blood_splatter_icon) + blood_splatter_icons[index] = blood_splatter_icon + overlays += blood_splatter_icon + return 1 //we applied blood to the item + +/obj/item/clothing/gloves/add_blood(mob/living/carbon/M) + if(!..()) + return 0 + transfer_blood = rand(2, 4) + bloody_hands_mob = M + return 1 + +/turf/add_blood(mob/living/carbon/human/M) + if(!..()) + return 0 + + var/obj/effect/decal/cleanable/blood/B = locate() in contents //check for existing blood splatter + if(!B) + blood_splatter(src,M.get_blood(M.vessel),1) + B = locate(/obj/effect/decal/cleanable/blood) in contents + B.add_blood_list(M) + return 1 //we bloodied the floor + +/mob/living/carbon/human/add_blood(mob/living/carbon/M) + if(!..()) + return 0 + add_blood_list(M) + bloody_hands = rand(2, 4) + bloody_hands_mob = M + update_inv_gloves() //handles bloody hands overlays and updating + return 1 //we applied blood to the item + +/atom/proc/rejects_blood() + return 0 + +// Only adds blood on the floor -- Skie +/atom/proc/add_blood_floor(mob/living/carbon/M) + if(istype(src, /turf)) + if(M.has_dna()) //mobs with dna = (monkeys + humans at time of writing) + var/obj/effect/decal/cleanable/blood/B = locate() in contents + if(!B) + blood_splatter(src,M,1) + B = locate(/obj/effect/decal/cleanable/blood) in contents + B.blood_DNA[M.dna.unique_enzymes] = M.dna.blood_type + else if(istype(M, /mob/living/carbon/alien)) + var/obj/effect/decal/cleanable/xenoblood/B = locate() in contents + if(!B) + B = new(src) + B.blood_DNA["UNKNOWN BLOOD"] = "X*" + else if(istype(M, /mob/living/silicon/robot)) + var/obj/effect/decal/cleanable/oil/B = locate() in contents + if(!B) + B = new(src) + +/atom/proc/clean_blood() + if(istype(blood_DNA, /list)) + blood_DNA = null + return 1 + + +/atom/proc/get_global_map_pos() + if(!islist(global_map) || isemptylist(global_map)) return + var/cur_x = null + var/cur_y = null + var/list/y_arr = null + for(cur_x=1,cur_x<=global_map.len,cur_x++) + y_arr = global_map[cur_x] + cur_y = y_arr.Find(src.z) + if(cur_y) + break +// world << "X = [cur_x]; Y = [cur_y]" + if(cur_x && cur_y) + return list("x"=cur_x,"y"=cur_y) + else + return 0 + +/atom/proc/isinspace() + if(istype(get_turf(src), /turf/open/space)) + return 1 + else + return 0 + +/atom/proc/handle_fall() + return + +/atom/proc/handle_slip() + return +/atom/proc/singularity_act() + return + +/atom/proc/singularity_pull() + return + +/atom/proc/acid_act(acidpwr, toxpwr, acid_volume) + return + +/atom/proc/emag_act() + return + +/atom/proc/narsie_act() + return + +/atom/proc/storage_contents_dump_act(obj/item/weapon/storage/src_object, mob/user) + return 0 + +//This proc is called on the location of an atom when the atom is Destroy()'d +/atom/proc/handle_atom_del(atom/A) + +// Byond seemingly calls stat, each tick. +// Calling things each tick can get expensive real quick. +// So we slow this down a little. +// See: http://www.byond.com/docs/ref/info.html#/client/proc/Stat +/atom/Stat() + . = ..() + sleep(1) + stoplag() + +//This is called just before maps and objects are initialized, use it to spawn other mobs/objects +//effects at world start up without causing runtimes +/atom/proc/spawn_atom_to_world() + +//This will be called after the map and objects are loaded +/atom/proc/initialize() + return + +//the vision impairment to give to the mob whose perspective is set to that atom (e.g. an unfocused camera giving you an impaired vision when looking through it) +/atom/proc/get_remote_view_fullscreens(mob/user) + return + +//the sight changes to give to the mob whose perspective is set to that atom (e.g. A mob with nightvision loses its nightvision while looking through a normal camera) +/atom/proc/update_remote_sight(mob/living/user) + return + +/atom/proc/add_vomit_floor(mob/living/carbon/M, toxvomit = 0) + if(istype(src,/turf) ) + var/obj/effect/decal/cleanable/vomit/V = PoolOrNew(/obj/effect/decal/cleanable/vomit, src) + // Make toxins vomit look different + if(toxvomit) + V.icon_state = "vomittox_[pick(1,4)]" + if(M.reagents) + clear_reagents_to_vomit_pool(M,V) + +/atom/proc/clear_reagents_to_vomit_pool(mob/living/carbon/M, obj/effect/decal/cleanable/vomit/V) + M.reagents.trans_to(V, M.reagents.total_volume / 10) + for(var/datum/reagent/R in M.reagents.reagent_list) //clears the stomach of anything that might be digested as food + if(istype(R, /datum/reagent/consumable)) + var/datum/reagent/consumable/nutri_check = R + if(nutri_check.nutriment_factor >0) + M.reagents.remove_reagent(R.id,R.volume) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index d6bce4278bf..f634e1eca8b 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -388,7 +388,7 @@ var/global/image/fire_overlay = image("icon" = 'icons/effects/fire.dmi', "icon_s /obj/item/proc/on_found(mob/finder) return -// called after an item is placed in an equipment slot +// called after an item is placed in an equipment slot //NOPE, for example, if you put a helmet in slot_head, it is NOT in user's head variable yet, how stupid. // user is mob that equipped it // slot uses the slot_X defines found in setup.dm // for items that can be placed in multiple slots diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm index e0b2c348d8f..83eed76aeee 100644 --- a/code/modules/clothing/head/misc.dm +++ b/code/modules/clothing/head/misc.dm @@ -62,14 +62,6 @@ desc = "A plastic replica of a Syndicate agent's space helmet. You'll look just like a real murderous Syndicate agent in this! This is a toy, it is not made for use in space!" flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR -/obj/item/clothing/head/cardborg - name = "cardborg helmet" - desc = "A helmet made out of a box." - icon_state = "cardborg_h" - item_state = "cardborg_h" - flags_cover = HEADCOVERSEYES - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR - /obj/item/clothing/head/snowman name = "Snowman Head" desc = "A ball of white styrofoam. So festive." diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm index 9b660fb015c..fb35aceb6b6 100644 --- a/code/modules/clothing/head/misc_special.dm +++ b/code/modules/clothing/head/misc_special.dm @@ -5,7 +5,7 @@ * Ushanka * Pumpkin head * Kitty ears - * + * Cardborg disguise */ /* @@ -147,3 +147,24 @@ flags_inv = 0 armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) brightness_on = 1 //luminosity when on + + +/obj/item/clothing/head/cardborg + name = "cardborg helmet" + desc = "A helmet made out of a box." + icon_state = "cardborg_h" + item_state = "cardborg_h" + flags_cover = HEADCOVERSEYES + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR + +/obj/item/clothing/head/cardborg/equipped(mob/living/user, slot) + ..() + if(ishuman(user) && slot == slot_head) + var/mob/living/carbon/human/H = user + if(istype(H.wear_suit, /obj/item/clothing/suit/cardborg)) + var/obj/item/clothing/suit/cardborg/CB = H.wear_suit + CB.disguise(user, src) + +/obj/item/clothing/head/cardborg/dropped(mob/living/user) + ..() + user.remove_alt_appearance("standard_borg_disguise") \ No newline at end of file diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 91f2d49a6c2..cd62fb539d3 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -155,7 +155,6 @@ allowed = list(/obj/item/weapon/storage/book/bible, /obj/item/weapon/nullrod, /obj/item/weapon/reagent_containers/food/drinks/bottle/holywater, /obj/item/weapon/storage/fancy/candle_box, /obj/item/candle, /obj/item/weapon/tank/internals/emergency_oxygen) - /obj/item/clothing/suit/cardborg name = "cardborg suit" desc = "An ordinary cardboard box with holes cut in the sides." @@ -164,6 +163,26 @@ body_parts_covered = CHEST|GROIN flags_inv = HIDEJUMPSUIT +/obj/item/clothing/suit/cardborg/equipped(mob/living/user, slot) + ..() + if(slot == slot_wear_suit) + disguise(user) + +/obj/item/clothing/suit/cardborg/dropped(mob/living/user) + ..() + user.remove_alt_appearance("standard_borg_disguise") + +/obj/item/clothing/suit/cardborg/proc/disguise(mob/living/carbon/human/H, obj/item/clothing/head/cardborg/borghead) + if(istype(H)) + if(!borghead) + borghead = H.head + if(istype(borghead, /obj/item/clothing/head/cardborg)) //why is this done this way? because equipped() is called BEFORE THE ITEM IS IN THE SLOT WHYYYY + var/image/I = image(icon = 'icons/mob/robots.dmi' , icon_state = "robot", loc = H) + I.override = 1 + I.overlays += image(icon = 'icons/mob/robots.dmi' , icon_state = "eyes-standard") //gotta look realistic + H.add_alt_appearance("standard_borg_disguise", I, silicon_mobs+H) //you look like a robot to robots! (including yourself because you're totally a robot) + + /obj/item/clothing/suit/snowman name = "snowman outfit" desc = "Two white spheres covered in white glitter. 'Tis the season." diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index c2aa78a467a..f25becac68c 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -35,6 +35,7 @@ /mob/living/silicon/New() ..() + silicon_mobs |= src var/datum/atom_hud/data/diagnostic/diag_hud = huds[DATA_HUD_DIAGNOSTIC] diag_hud.add_to_hud(src) diag_hud_set_status() @@ -43,6 +44,7 @@ /mob/living/silicon/Destroy() radio = null aicamera = null + silicon_mobs -= src return ..() /mob/living/silicon/contents_explosion(severity, target) diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index ed0e6f7dac4..1ce7aed14f9 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -64,3 +64,9 @@ winset(src, null, "mainwindow.macro=[macro_hotkeys] mapwindow.map.focus=true input.background-color=#e0e0e0") else winset(src, null, "mainwindow.macro=[macro_default] input.focus=true input.background-color=#d3b5b5") + + if(viewing_alternate_appearances && viewing_alternate_appearances.len) + for(var/aakey in viewing_alternate_appearances) + var/datum/alternate_appearance/AA = viewing_alternate_appearances[aakey] + if(AA) + AA.display_to(list(src)) diff --git a/tgstation.dme b/tgstation.dme index 56c7529a01b..1d122ea5942 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -233,6 +233,7 @@ #include "code\datums\wires\syndicatebomb.dm" #include "code\datums\wires\vending.dm" #include "code\datums\wires\wires.dm" +#include "code\game\alternate_appearance.dm" #include "code\game\asteroid.dm" #include "code\game\atoms.dm" #include "code\game\atoms_movable.dm"