Merge pull request #15962 from SandPoot/contextual-screentips

Contextual screentips -- Screentips now show you what items/objects can do
This commit is contained in:
Timothy Teakettle
2023-02-15 23:04:54 +00:00
committed by GitHub
33 changed files with 1007 additions and 24 deletions
+91 -5
View File
@@ -1450,11 +1450,97 @@
/atom/MouseEntered(location, control, params)
. = ..()
// Screentips
var/client/client = usr?.client
var/datum/hud/active_hud = client?.mob?.hud_used
var/mob/user = usr
if(isnull(user) && !user.client)
return
var/datum/hud/active_hud = user.hud_used
if(active_hud)
if(!client.prefs.screentip_pref || (flags_1 & NO_SCREENTIPS_1))
var/screentips_enabled = user.client.prefs.screentip_pref
if(screentips_enabled == SCREENTIP_PREFERENCE_DISABLED || (flags_1 & NO_SCREENTIPS_1))
active_hud.screentip_text.maptext = ""
else
//We inline a MAPTEXT() here, because there's no good way to statically add to a string like this
active_hud.screentip_text.maptext = MAPTEXT("<span style='text-align: center; font-size: 32px; color: [client?.prefs?.screentip_color]'>[name]</span>")
active_hud.screentip_text.maptext_y = 0
var/lmb_rmb_line = ""
var/ctrl_lmb_ctrl_rmb_line = ""
var/alt_lmb_alt_rmb_line = ""
var/shift_lmb_ctrl_shift_lmb_line = ""
var/extra_lines = 0
var/extra_context = ""
if (isliving(user) || isovermind(user) || isaicamera(user))
var/obj/item/held_item = user.get_active_held_item()
if (flags_1 & HAS_CONTEXTUAL_SCREENTIPS_1 || held_item?.item_flags & ITEM_HAS_CONTEXTUAL_SCREENTIPS)
var/list/context = list()
var/contextual_screentip_returns = \
SEND_SIGNAL(src, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, context, held_item, user) \
| (held_item && SEND_SIGNAL(held_item, COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET, context, src, user))
if (contextual_screentip_returns & CONTEXTUAL_SCREENTIP_SET)
// LMB and RMB on one line...
var/lmb_text = ""
if((SCREENTIP_CONTEXT_LMB in context) && (length(context[SCREENTIP_CONTEXT_LMB]) > 0))
lmb_text = build_context(context, SCREENTIP_CONTEXT_LMB)
var/rmb_text = ""
if((SCREENTIP_CONTEXT_RMB in context) && (length(context[SCREENTIP_CONTEXT_RMB]) > 0))
rmb_text = build_context(context, SCREENTIP_CONTEXT_RMB)
if (lmb_text)
lmb_rmb_line = lmb_text
if (rmb_text)
lmb_rmb_line += " | [rmb_text]"
else if (rmb_text)
lmb_rmb_line = rmb_text
// Ctrl-LMB, Ctrl-RMB on one line...
if (lmb_rmb_line != "")
lmb_rmb_line += "<br>"
extra_lines++
if((SCREENTIP_CONTEXT_CTRL_LMB in context) && (length(context[SCREENTIP_CONTEXT_CTRL_LMB]) > 0))
ctrl_lmb_ctrl_rmb_line = build_context(context, SCREENTIP_CONTEXT_CTRL_LMB)
if((SCREENTIP_CONTEXT_CTRL_RMB in context) && (length(context[SCREENTIP_CONTEXT_CTRL_RMB]) > 0))
if (ctrl_lmb_ctrl_rmb_line != "")
ctrl_lmb_ctrl_rmb_line += " | "
ctrl_lmb_ctrl_rmb_line += "[SCREENTIP_CONTEXT_CTRL_RMB]: [context[SCREENTIP_CONTEXT_CTRL_RMB]]"
ctrl_lmb_ctrl_rmb_line = build_context(context, SCREENTIP_CONTEXT_CTRL_RMB)
// Alt-LMB, Alt-RMB on one line...
if (ctrl_lmb_ctrl_rmb_line != "")
ctrl_lmb_ctrl_rmb_line += "<br>"
extra_lines++
if((SCREENTIP_CONTEXT_ALT_LMB in context) && (length(context[SCREENTIP_CONTEXT_ALT_LMB]) > 0))
alt_lmb_alt_rmb_line = build_context(context, SCREENTIP_CONTEXT_ALT_LMB)
if((SCREENTIP_CONTEXT_ALT_RMB in context) && (length(context[SCREENTIP_CONTEXT_ALT_RMB]) > 0))
if (alt_lmb_alt_rmb_line != "")
alt_lmb_alt_rmb_line += " | "
alt_lmb_alt_rmb_line = build_context(context, SCREENTIP_CONTEXT_ALT_RMB)
// Shift-LMB, Ctrl-Shift-LMB on one line...
if (alt_lmb_alt_rmb_line != "")
alt_lmb_alt_rmb_line += "<br>"
extra_lines++
if((SCREENTIP_CONTEXT_SHIFT_LMB in context) && (length(context[SCREENTIP_CONTEXT_SHIFT_LMB]) > 0))
shift_lmb_ctrl_shift_lmb_line = build_context(context, SCREENTIP_CONTEXT_SHIFT_LMB)
if((SCREENTIP_CONTEXT_CTRL_SHIFT_LMB in context) && (length(context[SCREENTIP_CONTEXT_CTRL_SHIFT_LMB]) > 0))
if (shift_lmb_ctrl_shift_lmb_line != "")
shift_lmb_ctrl_shift_lmb_line += " | "
shift_lmb_ctrl_shift_lmb_line += "[SCREENTIP_CONTEXT_CTRL_SHIFT_LMB]: [context[SCREENTIP_CONTEXT_CTRL_SHIFT_LMB]]"
shift_lmb_ctrl_shift_lmb_line = build_context(context, SCREENTIP_CONTEXT_CTRL_SHIFT_LMB)
if (shift_lmb_ctrl_shift_lmb_line != "")
extra_lines++
if(extra_lines)
extra_context = "<br><span style='font-size: 7px'>[lmb_rmb_line][ctrl_lmb_ctrl_rmb_line][alt_lmb_alt_rmb_line][shift_lmb_ctrl_shift_lmb_line]</span>"
//first extra line pushes atom name line up 10px, subsequent lines push it up 9px, this offsets that and keeps the first line in the same place
active_hud.screentip_text.maptext_y = -10 + (extra_lines - 1) * -9
if (screentips_enabled == SCREENTIP_PREFERENCE_CONTEXT_ONLY && extra_context == "")
active_hud.screentip_text.maptext = ""
else
//We inline a MAPTEXT() here, because there's no good way to statically add to a string like this
active_hud.screentip_text.maptext = "<span class='maptext' style='text-align: center; font-size: 32px; color: [user.client.prefs.screentip_color]'>[name][extra_context]</span>"
+64 -3
View File
@@ -697,6 +697,42 @@
. += "<span class='notice'>Alt-click [src] to [ secondsElectrified ? "un-electrify" : "permanently electrify"] it.</span>"
. += "<span class='notice'>Ctrl-Shift-click [src] to [ emergency ? "disable" : "enable"] emergency access.</span>"
/obj/machinery/door/airlock/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
. = ..()
if(istype(held_item, /obj/item/stack/sheet/plasteel))
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Reinforce")
return CONTEXTUAL_SCREENTIP_SET
switch (held_item?.tool_behaviour)
if (TOOL_CROWBAR)
if (panel_open)
if (security_level == AIRLOCK_SECURITY_PLASTEEL_O_S || security_level == AIRLOCK_SECURITY_PLASTEEL_I_S)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Remove shielding")
return CONTEXTUAL_SCREENTIP_SET
else if (should_try_removing_electronics())
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Remove electronics")
return CONTEXTUAL_SCREENTIP_SET
// Not always contextually true, but is contextually false in ways that make gameplay interesting.
// For example, trying to pry open an airlock, only for the bolts to be down and the lights off.
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Pry open")
return CONTEXTUAL_SCREENTIP_SET
if (TOOL_WELDER)
LAZYSET(context[SCREENTIP_CONTEXT_RMB], INTENT_ANY, "Weld shut")
if (panel_open)
switch (security_level)
if (AIRLOCK_SECURITY_METAL, AIRLOCK_SECURITY_PLASTEEL_I, AIRLOCK_SECURITY_PLASTEEL_O)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Cut shielding")
return CONTEXTUAL_SCREENTIP_SET
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Repair")
return CONTEXTUAL_SCREENTIP_SET
return .
/obj/machinery/door/airlock/attack_ai(mob/user)
if(!src.canAIControl(user))
if(src.canAIHack())
@@ -1024,6 +1060,31 @@
return
return !operating && density
/// Returns if a crowbar would remove the airlock electronics
/obj/machinery/door/airlock/proc/should_try_removing_electronics()
if (security_level != 0)
return FALSE
if (!panel_open)
return FALSE
if (obj_flags & EMAGGED)
return TRUE
if (!density)
return FALSE
if (!welded)
return FALSE
if (hasPower())
return FALSE
if (locked)
return FALSE
return TRUE
/obj/machinery/door/airlock/try_to_crowbar(obj/item/I, mob/living/user)
var/beingcrowbarred = null
if(I.tool_behaviour == TOOL_CROWBAR)
@@ -1042,9 +1103,9 @@
charge.forceMove(get_turf(user))
charge = null
return
if(beingcrowbarred && panel_open && ((obj_flags & EMAGGED) || (density && welded && !operating && !hasPower() && !locked)))
user.visible_message("[user] removes the electronics from the airlock assembly.", \
"<span class='notice'>You start to remove electronics from the airlock assembly...</span>")
if(beingcrowbarred && should_try_removing_electronics() && !operating)
user.visible_message(span_notice("[user] removes the electronics from the airlock assembly."), \
span_notice("You start to remove electronics from the airlock assembly..."))
if(I.use_tool(src, user, 40, volume=100))
deconstruct(TRUE, user)
return
+8
View File
@@ -55,6 +55,13 @@
if(!poddoor)
. += "<span class='notice'>Its maintenance panel is <b>screwed</b> in place.</span>"
/obj/machinery/door/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
. = ..()
if (isnull(held_item) && !istype(src, /obj/machinery/door/firedoor)) // You cannot open/close with your hands
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, (density ? "Open" : "Close"))
return CONTEXTUAL_SCREENTIP_SET
/obj/machinery/door/check_access_list(list/access_list)
if(red_alert_access && GLOB.security_level >= SEC_LEVEL_RED)
return TRUE
@@ -65,6 +72,7 @@
set_init_door_layer()
update_freelook_sight()
air_update_turf(1)
register_context()
GLOB.airlocks += src
spark_system = new /datum/effect_system/spark_spread
spark_system.set_up(2, 1, src)
+28
View File
@@ -46,6 +46,34 @@
else
. += "<span class='notice'>The bolt locks have been <i>unscrewed</i>, but the bolts themselves are still <b>wrenched</b> to the floor.</span>"
/obj/machinery/door/firedoor/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
. = ..()
if (isnull(held_item))
if (density)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Knock")
return CONTEXTUAL_SCREENTIP_SET
else
return .
switch (held_item.tool_behaviour)
if (TOOL_CROWBAR)
if(!welded)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, (density ? "Open" : "Close"))
return CONTEXTUAL_SCREENTIP_SET
if (TOOL_WELDER)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, (welded ? "Unweld shut" : "Weld shut"))
return CONTEXTUAL_SCREENTIP_SET
if (TOOL_WRENCH)
if (welded && !boltslocked)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Unfasten bolts")
return CONTEXTUAL_SCREENTIP_SET
if (TOOL_SCREWDRIVER)
if (welded)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, (boltslocked ? "Unlock bolts" : "Lock bolts"))
return CONTEXTUAL_SCREENTIP_SET
return .
/obj/machinery/door/firedoor/proc/CalculateAffectingAreas()
remove_from_areas()
affecting_areas = get_adjacent_open_areas(src) | get_base_area(src)
+10
View File
@@ -51,6 +51,16 @@
myarea = get_base_area(src)
LAZYADD(myarea.firealarms, src)
register_context()
/obj/machinery/firealarm/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
. = ..()
if(isnull(held_item))
var/area/location = get_area(src)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, (location.fire ? "Turn off" : "Turn on"))
return CONTEXTUAL_SCREENTIP_SET
/obj/machinery/firealarm/Destroy()
myarea.firereset(src)
LAZYREMOVE(myarea.firealarms, src)
@@ -116,6 +116,38 @@ GLOBAL_LIST_EMPTY(PDAs)
if(inserted_item && (!isturf(loc)))
. += "<span class='notice'>Ctrl-click to remove [inserted_item].</span>"
/obj/item/pda/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
. = ..()
if(held_item)
if(istype(held_item, /obj/item/cartridge) && !cartridge)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Insert Cartridge")
. = CONTEXTUAL_SCREENTIP_SET
else if(istype(held_item, /obj/item/card/id) && !id)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Insert ID")
. = CONTEXTUAL_SCREENTIP_SET
else if(istype(held_item, /obj/item/paicard) && !pai)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Insert pAI")
. = CONTEXTUAL_SCREENTIP_SET
else if(is_type_in_list(held_item, contained_item) && !inserted_item)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Insert [held_item]")
. = CONTEXTUAL_SCREENTIP_SET
else if(istype(held_item, /obj/item/photo))
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Scan photo")
. = CONTEXTUAL_SCREENTIP_SET
if(id) // ID gets removed before inserted_item
LAZYSET(context[SCREENTIP_CONTEXT_ALT_LMB], INTENT_ANY, "Remove ID")
. = CONTEXTUAL_SCREENTIP_SET
else if(inserted_item)
LAZYSET(context[SCREENTIP_CONTEXT_ALT_LMB], INTENT_ANY, "Remove [inserted_item]")
. = CONTEXTUAL_SCREENTIP_SET
if(inserted_item)
LAZYSET(context[SCREENTIP_CONTEXT_CTRL_LMB], INTENT_ANY, "Remove [inserted_item]")
. = CONTEXTUAL_SCREENTIP_SET
return . || NONE
/obj/item/pda/Initialize(mapload)
if(GLOB.pda_reskins)
unique_reskin = GLOB.pda_reskins
@@ -133,6 +165,8 @@ GLOBAL_LIST_EMPTY(PDAs)
new_overlays = TRUE
update_icon()
register_context()
/obj/item/pda/reskin_obj(mob/M)
. = ..()
new_overlays = TRUE
@@ -91,6 +91,11 @@ GENETICS SCANNER
var/scanmode = SCANMODE_HEALTH
var/advanced = FALSE
/obj/item/healthanalyzer/Initialize(mapload)
. = ..()
register_item_context()
/obj/item/healthanalyzer/suicide_act(mob/living/carbon/user)
user.visible_message("<span class='suicide'>[user] begins to analyze [user.p_them()]self with [src]! The display shows that [user.p_theyre()] dead!</span>")
return BRUTELOSS
@@ -130,6 +135,24 @@ GENETICS SCANNER
add_fingerprint(user)
/obj/item/healthanalyzer/add_item_context(
obj/item/source,
list/context,
atom/target,
)
if (!isliving(target))
return NONE
switch (scanmode)
if (SCANMODE_HEALTH)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Scan health")
if (SCANMODE_CHEMICAL)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Scan chemicals")
if (SCANMODE_WOUND)
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Scan wounds")
return CONTEXTUAL_SCREENTIP_SET
// Used by the PDA medical scanner too
/proc/healthscan(mob/user, mob/living/M, mode = 1, advanced = FALSE)
if(isliving(user) && (user.incapacitated() || user.eye_blind))
@@ -549,6 +572,7 @@ GENETICS SCANNER
name = "first aid analyzer"
icon_state = "adv_spectrometer"
desc = "A prototype MeLo-Tech medical scanner used to diagnose injuries and recommend treatment for serious wounds, but offers no further insight into the patient's health. You hope the final version is less annoying to read!"
scanmode = SCANMODE_WOUND // Forces context to give correct tip.
var/next_encouragement
var/greedy
+15
View File
@@ -122,6 +122,21 @@
breakouttime = 300 //Deciseconds = 30s
cuffsound = 'sound/weapons/cablecuff.ogg'
/obj/item/restraints/handcuffs/cable/Initialize(mapload)
. = ..()
var/static/list/hovering_item_typechecks = list(
/obj/item/stack/rods = list(
SCREENTIP_CONTEXT_LMB = list(INTENT_ANY = "Craft wired rod"),
),
/obj/item/stack/sheet/metal = list(
SCREENTIP_CONTEXT_LMB = list(INTENT_ANY = "Craft bola"),
),
)
AddElement(/datum/element/contextual_screentip_item_typechecks, hovering_item_typechecks)
/obj/item/restraints/handcuffs/cable/attack_self(mob/user)
to_chat(user, "<span class='notice'>You start unwinding the cable restraints back into coil</span>")
if(!do_after(user, 25, TRUE, user))
+22
View File
@@ -54,6 +54,8 @@
cell = new preload_cell_type(src)
update_icon()
register_item_context()
/obj/item/melee/baton/DoRevenantThrowEffects(atom/target)
switch_status()
@@ -155,6 +157,26 @@
if(!interrupt)
return ..()
/obj/item/melee/baton/add_item_context(datum/source, list/context, atom/target, mob/living/user)
if (isturf(target))
return NONE
if (isobj(target))
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Attack")
else
if (turned_on)
LAZYSET(context[SCREENTIP_CONTEXT_RMB], INTENT_ANY, "Knockdown")
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Stun")
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_HARM, "Harmful stun")
else
LAZYSET(context[SCREENTIP_CONTEXT_RMB], INTENT_ANY, "Knockdown") // DON'T TELL EM, PRANKED.
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_ANY, "Stun") // STILL DO NOT DARE TELLING THEM
LAZYSET(context[SCREENTIP_CONTEXT_LMB], INTENT_HARM, "Attack") // It's fine i guess...?
return CONTEXTUAL_SCREENTIP_SET
/obj/item/melee/baton/alt_pre_attack(atom/A, mob/living/user, params)
if(!user.CheckActionCooldown(CLICK_CD_MELEE))
return
+15
View File
@@ -583,6 +583,21 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
attack_verb = list("hit", "bludgeoned", "whacked", "bonked")
wound_bonus = -10
/obj/item/wirerod/Initialize(mapload)
. = ..()
var/static/list/hovering_item_typechecks = list(
/obj/item/shard = list(
SCREENTIP_CONTEXT_LMB = list(INTENT_ANY = "Craft spear"),
),
/obj/item/assembly/igniter = list(
SCREENTIP_CONTEXT_LMB = list(INTENT_ANY = "Craft stunprod"),
),
)
AddElement(/datum/element/contextual_screentip_item_typechecks, hovering_item_typechecks)
/obj/item/wirerod/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/shard))
var/obj/item/spear/S = new /obj/item/spear
+24 -1
View File
@@ -37,6 +37,16 @@
smooth = SMOOTH_TRUE
canSmoothWith = list(/obj/structure/table, /obj/structure/table/reinforced, /obj/structure/table/greyscale)
/obj/structure/table/Initialize(mapload)
. = ..()
var/static/list/barehanded_interactions = list(
INTENT_ANY = "Slap",
INTENT_HARM = "Slam"
)
AddElement(/datum/element/contextual_screentip_bare_hands, rmb_text_combat_mode = barehanded_interactions)
/obj/structure/table/examine(mob/user)
. = ..()
. += deconstruction_hints(user)
@@ -490,6 +500,19 @@
// the sprites in the editor to see why.
icon = smooth_icon
if (!(flags_1 & NODECONSTRUCT_1))
var/static/list/tool_behaviors = list(
TOOL_SCREWDRIVER = list(
SCREENTIP_CONTEXT_LMB = list(INTENT_ANY = "Disassemble"),
),
TOOL_WRENCH = list(
SCREENTIP_CONTEXT_LMB = list(INTENT_ANY = "Deconstruct"),
),
)
AddElement(/datum/element/contextual_screentip_tools, tool_behaviors)
/obj/structure/table/wood/fancy/black
icon_state = "fancy_table_black"
buildstack = /obj/item/stack/tile/carpet/black
@@ -704,7 +727,7 @@
. = !density
if(istype(caller))
. = . || (caller.pass_flags & PASSTABLE)
/obj/structure/rack/MouseDrop_T(obj/O, mob/user)
. = ..()
if ((!( istype(O, /obj/item) ) || user.get_active_held_item() != O))