diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index ffc637b21bc..8fb5232099e 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -36,8 +36,6 @@ #define isprojectile(A) (istype(A, /obj/item/projectile)) -#define is_cleanable(A) (istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/rune)) //if something is cleanable - #define is_pen(W) (istype(W, /obj/item/pen)) #define is_pda(W) (istype(W, /obj/item/pda)) diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index 067da4146ec..286f1d04aad 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -89,6 +89,8 @@ #define HIGH_LANDMARK_LAYER 9.2 #define AREA_LAYER 10 #define MASSIVE_OBJ_LAYER 11 + +#define POINT_PLANE 14 #define POINT_LAYER 12 #define CHAT_LAYER 12.0001 // Do not insert layers between these two values diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 03a9b25044c..acb44796abc 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -31,6 +31,10 @@ var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move var/list/client_mobs_in_contents + /// Icon state for thought bubbles. Normally set by mobs. + var/thought_bubble_image = "thought_bubble" + + /atom/movable/attempt_init(loc, ...) var/turf/T = get_turf(src) if(T && SSatoms.initialized != INITIALIZATION_INSSATOMS && GLOB.space_manager.is_zlevel_dirty(T.z)) diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm index af6d3598d75..02f3c47f824 100644 --- a/code/game/gamemodes/cult/runes.dm +++ b/code/game/gamemodes/cult/runes.dm @@ -130,6 +130,8 @@ To draw a rune, use a ritual dagger. visible_message("[src] suddenly appears!") alpha = initial(alpha) +/obj/effect/rune/is_cleanable() + return TRUE /* There are a few different procs each rune runs through when a cultist activates it. diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm index 4636e9cd449..9521f1d4c92 100644 --- a/code/game/gamemodes/nuclear/pinpointer.dm +++ b/code/game/gamemodes/nuclear/pinpointer.dm @@ -108,7 +108,7 @@ if(!the_s_bomb) the_s_bomb = locate() -/obj/item/pinpointer/proc/point_at(atom/target) +/obj/item/pinpointer/proc/pinpoint_at(atom/target) if(!target) icon_state = icon_null return @@ -132,15 +132,15 @@ /obj/item/pinpointer/proc/workdisk() scandisk() - point_at(the_disk) + pinpoint_at(the_disk) /obj/item/pinpointer/proc/workbomb() if(!syndicate) scanbomb() - point_at(the_bomb) + pinpoint_at(the_bomb) else scanbomb() - point_at(the_s_bomb) + pinpoint_at(the_s_bomb) /obj/item/pinpointer/examine(mob/user) . = ..() @@ -163,13 +163,13 @@ if(SETTING_DISK) workdisk() if(SETTING_LOCATION) - point_at(location) + pinpoint_at(location) if(SETTING_OBJECT) - point_at(target) + pinpoint_at(target) /obj/item/pinpointer/advpinpointer/workdisk() //since mode works diffrently for advpinpointer scandisk() - point_at(the_disk) + pinpoint_at(the_disk) /obj/item/pinpointer/advpinpointer/AltClick(mob/user) . = ..() @@ -280,7 +280,7 @@ visible_message("Shuttle Locator mode actived.") //Lets the mob holding it know that the mode has changed return //Get outta here scandisk() - point_at(the_disk) + pinpoint_at(the_disk) /obj/item/pinpointer/nukeop/workbomb() if(GLOB.bomb_set) //If the bomb is set, lead to the shuttle @@ -290,7 +290,7 @@ visible_message("Shuttle Locator mode actived.") //Lets the mob holding it know that the mode has changed return //Get outta here scanbomb() - point_at(the_s_bomb) + pinpoint_at(the_s_bomb) /obj/item/pinpointer/nukeop/proc/worklocation() if(!GLOB.bomb_set) @@ -307,7 +307,7 @@ if(loc.z != home.z) //If you are on a different z-level from the shuttle icon_state = icon_null else - point_at(home) + pinpoint_at(home) /obj/item/pinpointer/operative name = "operative pinpointer" @@ -340,7 +340,7 @@ /obj/item/pinpointer/operative/proc/workop() if(mode == MODE_OPERATIVE) scan_for_ops() - point_at(nearest_op, FALSE) + pinpoint_at(nearest_op, FALSE) else return FALSE @@ -375,7 +375,7 @@ /obj/item/pinpointer/ninja/proc/workninja() scan_for_ninja() - point_at(nearest_ninja, FALSE) + pinpoint_at(nearest_ninja, FALSE) /obj/item/pinpointer/ninja/examine(mob/user) . = ..() @@ -413,9 +413,9 @@ /obj/item/pinpointer/crew/process() if(mode == MODE_CREW && target_set) - point_at(target) + pinpoint_at(target) -/obj/item/pinpointer/crew/point_at(atom/target) +/obj/item/pinpointer/crew/pinpoint_at(atom/target) if(!target || !trackable(target)) icon_state = icon_null return @@ -490,9 +490,9 @@ /obj/item/pinpointer/thief/process() switch(setting) if(SETTING_LOCATION) - point_at(location) + pinpoint_at(location) if(SETTING_OBJECT) - point_at(target) + pinpoint_at(target) /obj/item/pinpointer/thief/cycle(mob/user) @@ -699,14 +699,14 @@ /obj/item/pinpointer/tendril/process() if(mode == MODE_TENDRIL) find_tendril() - point_at(target, FALSE) + pinpoint_at(target, FALSE) else icon_state = icon_off /obj/item/pinpointer/tendril/proc/find_tendril() if(mode == MODE_TENDRIL) scan_for_tendrils() - point_at(target) + pinpoint_at(target) else return FALSE diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm index f7c010f6ccf..ab9d10c954a 100644 --- a/code/game/objects/effects/decals/cleanable.dm +++ b/code/game/objects/effects/decals/cleanable.dm @@ -53,6 +53,9 @@ /obj/effect/decal/cleanable/proc/can_bloodcrawl_in() return FALSE +/obj/effect/decal/cleanable/is_cleanable() + return TRUE + /obj/effect/decal/cleanable/Initialize(mapload) . = ..() if(loc && isturf(loc)) diff --git a/code/game/objects/effects/effects.dm b/code/game/objects/effects/effects.dm index 12027879d0b..0bbace48d63 100644 --- a/code/game/objects/effects/effects.dm +++ b/code/game/objects/effects/effects.dm @@ -6,7 +6,7 @@ icon = 'icons/effects/effects.dmi' resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF move_resist = INFINITY - anchored = 1 + anchored = TRUE can_be_hit = FALSE /obj/effect/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) @@ -22,6 +22,9 @@ /obj/effect/acid_act() return +/obj/effect/proc/is_cleanable() //Called when you want to clean something, and usualy delete it after + return FALSE + /obj/effect/mech_melee_attack(obj/mecha/M) return 0 @@ -55,6 +58,7 @@ density = FALSE icon = null icon_state = null + armor = list(MELEE = 100, BULLET = 100, LASER = 100, ENERGY = 100, BOMB = 100, BIO = 100, RAD = 100, FIRE = 100, ACID = 100) // Most of these overrides procs below are overkill, but better safe than sorry. /obj/effect/abstract/swarmer_act() @@ -81,6 +85,15 @@ /obj/effect/abstract/ex_act(severity) return +/obj/effect/abstract/blob_act() + return + +/obj/effect/abstract/acid_act() + return + +/obj/effect/abstract/fire_act() + return + /obj/effect/decal plane = FLOOR_PLANE resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index 6dbd6d6b8e9..e8ba25e2089 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -1,12 +1,3 @@ -/obj/effect/temp_visual/point - name = "arrow" - desc = "It's an arrow hanging in mid-air. There may be a wizard about." - icon = 'icons/mob/screen_gen.dmi' - icon_state = "arrow" - layer = POINT_LAYER - duration = 20 - randomdir = FALSE - /obj/effect/temp_visual/dir_setting/bloodsplatter icon = 'icons/effects/blood.dmi' duration = 5 diff --git a/code/game/objects/items/weapons/mop.dm b/code/game/objects/items/weapons/mop.dm index d9085eb6764..5593fe302d7 100644 --- a/code/game/objects/items/weapons/mop.dm +++ b/code/game/objects/items/weapons/mop.dm @@ -39,7 +39,7 @@ if(reagents.has_reagent("water", 1) || reagents.has_reagent("cleaner", 1) || reagents.has_reagent("holywater", 1)) A.clean_blood() for(var/obj/effect/O in A) - if(is_cleanable(O)) + if(O.is_cleanable()) qdel(O) reagents.reaction(A, REAGENT_TOUCH, 10) //10 is the multiplier for the reaction effect. probably needed to wet the floor properly. reagents.remove_any(1) //reaction() doesn't use up the reagents diff --git a/code/game/objects/items/weapons/soap.dm b/code/game/objects/items/weapons/soap.dm index cbea25203e7..c3ad7d3664f 100644 --- a/code/game/objects/items/weapons/soap.dm +++ b/code/game/objects/items/weapons/soap.dm @@ -49,7 +49,7 @@ /obj/item/soap/proc/clean_turf(turf/simulated/T) T.clean_blood() for(var/obj/effect/O in T) - if(is_cleanable(O)) + if(O.is_cleanable()) qdel(O) /obj/item/soap/attack(mob/target as mob, mob/user as mob) diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 59b4cc47a1a..82934cd6bf5 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -453,7 +453,7 @@ var/turf/tile = loc loc.clean_blood() for(var/obj/effect/E in tile) - if(is_cleanable(E)) + if(E.is_cleanable()) qdel(E) /obj/machinery/shower/process() diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 36d26121c7c..954485ae7e0 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -3637,7 +3637,7 @@ N.mode = 3 //MODE_ADV, not defined here N.setting = 2 //SETTING_OBJECT, not defined here N.target = H - N.point_at(N.target) + N.pinpoint_at(N.target) N.modelocked = TRUE if(!locate(/obj/item/implant/dust, hunter_mob)) var/obj/item/implant/dust/D = new /obj/item/implant/dust(hunter_mob) diff --git a/code/modules/antagonists/traitor/contractor/items/contractor_pinpointer.dm b/code/modules/antagonists/traitor/contractor/items/contractor_pinpointer.dm index cff025b7ea6..1d7f33f564f 100644 --- a/code/modules/antagonists/traitor/contractor/items/contractor_pinpointer.dm +++ b/code/modules/antagonists/traitor/contractor/items/contractor_pinpointer.dm @@ -13,7 +13,7 @@ /// The first person to have used the item. If this is set already, no one else can use it. var/mob/owner = null -/obj/item/pinpointer/crew/contractor/point_at(atom/target) +/obj/item/pinpointer/crew/contractor/pinpoint_at(atom/target) if(target && trackable(target)) // Calc dir var/turf/T = get_turf(target) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index baed069f0d6..9776af07f1f 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -675,19 +675,23 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp /mob/dead/observer/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, ignore_lying = FALSE) return TRUE -//this is a mob verb instead of atom for performance reasons -//see /mob/verb/examinate() in mob.dm for more info -//overriden here and in /mob/living for different point span classes and sanity checks -/mob/dead/observer/run_pointed(atom/A as mob|obj|turf) + +/** + * This is a mob verb instead of atom for performance reasons. + * See /mob/verb/examinate() in mob.dm for more info. + * Overriden here and in /mob/living for different point span classes and sanity checks. + */ +/mob/dead/observer/run_pointed(atom/target) if(!..()) return FALSE var/follow_link if(invisibility) // Only show the button if the ghost is not visible to the living - follow_link = " ([ghost_follow_link(A, src)])" - usr.visible_message("[src] points to [A][follow_link].") - add_deadchat_logs(src, "point to [key_name(A)] [COORD(A)]") + follow_link = " ([ghost_follow_link(target, src)])" + usr.visible_message(span_deadsay("[src] points to [target][follow_link].")) + add_deadchat_logs(src, "point to [key_name(target)] [COORD(target)]") return TRUE + /mob/dead/observer/proc/incarnate_ghost() if(!client) return diff --git a/code/modules/mob/living/carbon/brain/brain_item.dm b/code/modules/mob/living/carbon/brain/brain_item.dm index 93bbd8aced4..e5915dbedb0 100644 --- a/code/modules/mob/living/carbon/brain/brain_item.dm +++ b/code/modules/mob/living/carbon/brain/brain_item.dm @@ -83,6 +83,7 @@ if(ishuman(owner)) owner.update_hair() + owner.thought_bubble_image = initial(owner.thought_bubble_image) . = ..() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index fb88fc03a2d..c1e17c8380a 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -309,24 +309,43 @@ return FALSE return ..() -/mob/living/run_pointed(atom/A) + +/mob/living/run_pointed(atom/target) if(!..()) return FALSE + var/obj/item/hand_item = get_active_hand() - if(istype(hand_item, /obj/item/gun) && A != hand_item) - if(a_intent == INTENT_HELP || !ismob(A)) - visible_message("[src.declent_ru(NOMINATIVE)] указыва[pluralize_ru(src.gender,"ет","ют")] [hand_item.declent_ru(INSTRUMENTAL)] на [A.declent_ru(ACCUSATIVE)]") - add_emote_logs(src, "point [hand_item] to [key_name(A)] [COORD(A)]") + var/pointed_object = "[target.declent_ru(ACCUSATIVE)]" + + if(target.loc in src) + var/atom/inside = target.loc + pointed_object += " внутри [inside.declent_ru(GENITIVE)]" + + if(istype(hand_item, /obj/item/gun) && target != hand_item) + if(a_intent == INTENT_HELP || !ismob(target)) + visible_message("[declent_ru(NOMINATIVE)] указыва[pluralize_ru(gender,"ет","ют")] [hand_item.declent_ru(INSTRUMENTAL)] на [pointed_object].") return TRUE - A.visible_message("[src.declent_ru(NOMINATIVE)] указыва[pluralize_ru(src.gender,"ет","ют")] [hand_item.declent_ru(INSTRUMENTAL)] на [A.declent_ru(ACCUSATIVE)]!", - "[src.declent_ru(NOMINATIVE)] указыва[pluralize_ru(src.gender,"ет","ют")] [hand_item.declent_ru(INSTRUMENTAL)] на [pluralize_ru(A.gender,"тебя","вас")]!") - A << 'sound/weapons/targeton.ogg' - add_emote_logs(src, "point [hand_item] HARM to [key_name(A)] [COORD(A)]") + + target.visible_message( + span_danger("[declent_ru(NOMINATIVE)] указыва[pluralize_ru(src.gender,"ет","ют")] [hand_item.declent_ru(INSTRUMENTAL)] на [pointed_object]!"), + span_userdanger("[declent_ru(NOMINATIVE)] указыва[pluralize_ru(src.gender,"ет","ют")] [hand_item.declent_ru(INSTRUMENTAL)] на [pluralize_ru(target.gender,"тебя","вас")]!"), + ) + SEND_SOUND(target, sound('sound/weapons/targeton.ogg')) + add_emote_logs(src, "point [hand_item] HARM to [key_name(target)] [COORD(target)]") return TRUE - visible_message("[src.declent_ru(NOMINATIVE)] указыва[pluralize_ru(src.gender,"ет","ют")] на [A.declent_ru(ACCUSATIVE)]") - add_emote_logs(src, "point to [key_name(A)] [COORD(A)]") + + if(istype(hand_item, /obj/item/toy/russian_revolver/trick_revolver) && target != hand_item) + var/obj/item/toy/russian_revolver/trick_revolver/trick = hand_item + visible_message(span_danger("[declent_ru(NOMINATIVE)] указыва[pluralize_ru(src.gender,"ет","ют")] [trick.declent_ru(INSTRUMENTAL)] на... и [trick.declent_ru(NOMINATIVE)] срабатывает у [genderize_ru(gender, "него","неё","него","них")] в руке!")) + trick.shoot_gun(src) + add_emote_logs(src, "point to [key_name(target)] [COORD(target)]") + return TRUE + + visible_message("[declent_ru(NOMINATIVE)] указыва[pluralize_ru(gender,"ет","ют")] на [pointed_object].") + add_emote_logs(src, "point to [key_name(target)] [COORD(target)]") return TRUE + /mob/living/verb/succumb() set hidden = 1 if(InCritical()) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 44d51f2b8ee..8f9b5c359fa 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1341,13 +1341,14 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(stat != DEAD && isturf(tile)) var/floor_only = TRUE for(var/A in tile) - if(istype(A, /obj/effect)) - if(is_cleanable(A)) - var/obj/effect/decal/cleanable/blood/B = A + if(iseffect(A)) + var/obj/effect/check = A + if(check.is_cleanable()) + var/obj/effect/decal/cleanable/blood/B = check if(istype(B) && B.off_floor) floor_only = FALSE else - qdel(A) + qdel(B) else if(istype(A, /obj/item)) var/obj/item/cleaned_item = A cleaned_item.clean_blood() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 56e76c161fc..f6f4367f38d 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -314,40 +314,6 @@ var/list/result = A.examine(src) to_chat(src, "
[result.Join("\n")]
") -//same as above -//note: ghosts can point, this is intended -//visible_message will handle invisibility properly -//overriden here and in /mob/dead/observer for different point span classes and sanity checks -/mob/verb/pointed(atom/A as mob|obj|turf in view(client.maxview())) - set name = "Point To" - set category = "Object" - - if(next_move >= world.time) - return - if(!isturf(loc) || istype(A, /obj/effect/temp_visual/point)) - return FALSE - - DEFAULT_QUEUE_OR_CALL_VERB(VERB_CALLBACK(src, PROC_REF(run_pointed), A)) - -/// possibly delayed verb that finishes the pointing process starting in [/mob/verb/pointed()]. -/// either called immediately or in the tick after pointed() was called, as per the [DEFAULT_QUEUE_OR_CALL_VERB()] macro -/mob/proc/run_pointed(atom/A) - if(client && !(A in view(client.maxview()))) - return FALSE - - changeNext_move(CLICK_CD_POINT) - - var/tile = get_turf(A) - if(!tile) - return FALSE - var/obj/P = new /obj/effect/temp_visual/point(tile) - P.invisibility = invisibility - if(get_turf(src) != tile) - // Start off from the pointer and make it slide to the pointee - P.pixel_x = (x - A.x) * 32 - P.pixel_y = (y - A.y) * 32 - animate(P, 0.5 SECONDS, pixel_x = A.pixel_x, pixel_y = A.pixel_y, easing = QUAD_EASING) - return TRUE /mob/proc/ret_grab(obj/effect/list_container/mobl/L as obj, flag) if((!( istype(l_hand, /obj/item/grab) ) && !( istype(r_hand, /obj/item/grab) ))) diff --git a/code/modules/point/point.dm b/code/modules/point/point.dm new file mode 100644 index 00000000000..96599da7187 --- /dev/null +++ b/code/modules/point/point.dm @@ -0,0 +1,180 @@ +#define POINT_TIME (2.5 SECONDS) +#define BUBBLE_TIME (3 SECONDS) + + +/** + * Point at an atom + * + * Intended to enable and standardise the pointing animation for all atoms + * + * Not intended as a replacement for the mob verb + */ +/atom/movable/proc/point_at(atom/pointed_atom) + var/turf/source_turf = loc + if(!isturf(source_turf)) + return + + if((pointed_atom in src) || (pointed_atom.loc in src)) + create_point_bubble_from_atom(pointed_atom) + return + + var/turf/pointed_turf = get_turf(pointed_atom) + if(!pointed_turf) + return + + var/obj/visual = new /obj/effect/temp_visual/point(source_turf, invisibility) + animate(visual, pixel_x = (pointed_turf.x - source_turf.x) * world.icon_size + pointed_atom.pixel_x, pixel_y = (pointed_turf.y - source_turf.y) * world.icon_size + pointed_atom.pixel_y, time = 0.5 SECONDS, easing = QUAD_EASING) + + +/** + * Create a bubble pointing at a particular icon and icon state. + * See args for create_point_bubble_from_atom. + */ +/atom/movable/proc/create_point_bubble(mutable_appearance/pointed_atom_appearance, include_arrow = TRUE) + var/obj/effect/thought_bubble_effect = new + + pointed_atom_appearance.layer = POINT_LAYER + pointed_atom_appearance.blend_mode = BLEND_INSET_OVERLAY + pointed_atom_appearance.pixel_x = 0 + pointed_atom_appearance.pixel_y = 0 + + var/mutable_appearance/thought_bubble = mutable_appearance( + 'icons/effects/effects.dmi', + thought_bubble_image, + layer = POINT_LAYER, + appearance_flags = KEEP_APART, + ) + + thought_bubble.overlays += pointed_atom_appearance + + thought_bubble.pixel_x = 16 + thought_bubble.pixel_y = 32 + thought_bubble.alpha = 200 + thought_bubble.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + + if(include_arrow) + var/mutable_appearance/point_visual = mutable_appearance( + 'icons/mob/screen_gen.dmi', + "arrow", + layer = thought_bubble.layer, + ) + point_visual.pixel_y = 7 + thought_bubble.overlays += point_visual + thought_bubble.plane = POINT_PLANE + + // vis_contents is used to preserve mouse opacity + thought_bubble_effect.appearance = thought_bubble + vis_contents += thought_bubble_effect + + thought_bubble_effect.alpha = 0 + animate(thought_bubble_effect, alpha = 255, time = 0.5 SECONDS, easing = EASE_OUT) + animate(alpha = 255, time = BUBBLE_TIME) + animate(alpha = 0, time = 0.5 SECONDS, easing = EASE_IN) + + QDEL_IN(thought_bubble_effect, BUBBLE_TIME + 1 SECONDS) + + +/** + * Create a point bubble towards a given item. + * + * Arguments: + * * pointed_atom - Atom to show in the bubble. + * * include_arrow - If true, show an arrow pointing downwards. + */ +/atom/movable/proc/create_point_bubble_from_atom(atom/pointed_atom, include_arrow = TRUE) + var/mutable_appearance/pointed_atom_appearance = new(pointed_atom.appearance) + + var/hover_outline_index = pointed_atom.get_filter("hover_outline") + if(!isnull(hover_outline_index)) + pointed_atom_appearance.filters.Cut(hover_outline_index, hover_outline_index + 1) + + create_point_bubble(pointed_atom_appearance, include_arrow) + + +/** + * Create a point bubble towards a given item, from an icon/icon state. + * + * Arguments: + * * icon - Icon source for the bubble's icon. + * * icon_state - Icon state for the bubble's icon. + * * include_arrow - If true, show an arrow pointing downwards. + */ +/atom/movable/proc/create_point_bubble_from_icons(icon, icon_state, include_arrow = TRUE) + var/mutable_appearance/pointed_atom_appearance = mutable_appearance( + icon, + icon_state, + ) + create_point_bubble(pointed_atom_appearance, include_arrow) + + +/** + * See above, this uses an uninstantiated path. + */ +/atom/movable/proc/create_point_bubble_from_path(atom/pointed_atom_path, include_arrow = TRUE) + create_point_bubble_from_icons(initial(pointed_atom_path.icon), initial(pointed_atom_path.icon_state), include_arrow) + + +/obj/effect/temp_visual/point + name = "arrow" + desc = "It's an arrow hanging in mid-air. There may be a wizard about." + icon = 'icons/mob/screen_gen.dmi' + icon_state = "arrow" + layer = POINT_LAYER + plane = POINT_PLANE + duration = POINT_TIME + randomdir = FALSE + + +/obj/effect/temp_visual/point/Initialize(mapload, set_invis = 0) + . = ..() + invisibility = set_invis + + +/** + * Point at an atom + * + * mob verbs are faster than object verbs. See + * [this byond forum post](https://secure.byond.com/forum/?post=1326139&page=2#comment8198716) + * for why this isn't atom/verb/pointed() + * + * note: ghosts can point, this is intended + * + * visible_message will handle invisibility properly + * + * overridden here and in /mob/dead/observer for different point span classes and sanity checks + */ +/mob/verb/pointed(atom/target as mob|obj|turf in view(client.view, src)) + set name = "Point To" + set category = null + + if(next_move >= world.time) + return + + if(istype(target, /obj/effect/temp_visual/point)) + return + + changeNext_move(CLICK_CD_POINT) + + DEFAULT_QUEUE_OR_CALL_VERB(VERB_CALLBACK(src, PROC_REF(run_pointed), target)) + + +/** + * Possibly delayed verb that finishes the pointing process starting in [/mob/verb/pointed()]. + * Either called immediately or in the tick after pointed() was called, as per the [DEFAULT_QUEUE_OR_CALL_VERB()] macro. + */ +/mob/proc/run_pointed(atom/target) + if(target.loc in src) // Object is inside a container on the mob. It's not part of the verb's list since it's not in view and requires middle clicking. + point_at(target) + return TRUE + + if(client && !(target in view(client.maxview(), src))) + return FALSE + + point_at(target) + + return TRUE + + +#undef POINT_TIME +#undef BUBBLE_TIME + diff --git a/code/modules/reagents/chemistry/reagents/water.dm b/code/modules/reagents/chemistry/reagents/water.dm index 1b6e9a91087..ae147aec92f 100644 --- a/code/modules/reagents/chemistry/reagents/water.dm +++ b/code/modules/reagents/chemistry/reagents/water.dm @@ -61,11 +61,14 @@ GLOBAL_LIST_INIT(diseases_carrier_reagents, list( process_flags = ORGANIC | SYNTHETIC taste_description = "floor cleaner" + /datum/reagent/space_cleaner/reaction_obj(obj/O, volume) - if(is_cleanable(O)) - var/obj/effect/decal/cleanable/blood/B = O - if(!(istype(B) && B.off_floor)) - qdel(O) + if(iseffect(O)) + var/obj/effect/E = O + if(E.is_cleanable()) + var/obj/effect/decal/cleanable/blood/B = E + if(!(istype(B) && B.off_floor)) + qdel(E) else if(O.simulated) O.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) @@ -74,6 +77,7 @@ GLOBAL_LIST_INIT(diseases_carrier_reagents, list( H.helmet.remove_atom_colour(WASHABLE_COLOUR_PRIORITY) O.clean_blood() + /datum/reagent/space_cleaner/reaction_turf(turf/T, volume) if(volume >= 1) var/floor_only = TRUE diff --git a/code/modules/surgery/organs/mmi_holder.dm b/code/modules/surgery/organs/mmi_holder.dm index 9e61f706f0d..b4e48c616d8 100644 --- a/code/modules/surgery/organs/mmi_holder.dm +++ b/code/modules/surgery/organs/mmi_holder.dm @@ -13,6 +13,11 @@ ..() // To supersede the over-writing of the MMI's name from `insert` update_from_mmi() + target.thought_bubble_image = "thought_bubble_machine" + if(ishuman(target) && istype(stored_mmi?.held_brain, /obj/item/organ/internal/brain/cluwne)) + var/mob/living/carbon/human/h_target = target + h_target.makeCluwne() //No matter where you go, no matter what you do, you cannot escape + /obj/item/organ/internal/brain/mmi_holder/remove(mob/living/user, special = ORGAN_MANIPULATION_DEFAULT) if(!special) diff --git a/code/modules/vehicle/janicart.dm b/code/modules/vehicle/janicart.dm index 1a94dff8b8f..b0b28c7f4d7 100644 --- a/code/modules/vehicle/janicart.dm +++ b/code/modules/vehicle/janicart.dm @@ -36,9 +36,9 @@ var/turf/tile = loc if(isturf(tile)) tile.clean_blood() - for(var/A in tile) - if(is_cleanable(A)) - qdel(A) + for(var/obj/effect/check in tile) + if(check.is_cleanable()) + qdel(check) /obj/vehicle/janicart/examine(mob/user) . = ..() diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index f5758e87583..a386dc0e9e0 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/paradise.dme b/paradise.dme index 7a0c67440f3..1051285607f 100644 --- a/paradise.dme +++ b/paradise.dme @@ -2525,6 +2525,7 @@ #include "code\modules\pda\radio.dm" #include "code\modules\pda\utilities.dm" #include "code\modules\persistence\persistence.dm" +#include "code\modules\point\point.dm" #include "code\modules\power\apc.dm" #include "code\modules\power\cable.dm" #include "code\modules\power\cable_logic.dm"