Add UI preference menus for AI hologram and status displays (#75740)

This adds two new dropdown menus for AI preferences with holograms and
status displays. It also sets these preferences if admins transform a
player into an AI.
This commit is contained in:
Tim
2023-06-23 08:43:26 +00:00
committed by GitHub
parent d93e81c2fc
commit 2a19963297
16 changed files with 256 additions and 106 deletions
+20
View File
@@ -600,6 +600,26 @@
#define AI_EMOTION_BLUE_GLOW "Blue Glow"
#define AI_EMOTION_RED_GLOW "Red Glow"
///Defines for AI hologram preferences
#define AI_HOLOGRAM_BEAR "Bear"
#define AI_HOLOGRAM_CARP "Carp"
#define AI_HOLOGRAM_CAT "Cat"
#define AI_HOLOGRAM_CAT_2 "Cat Alternate"
#define AI_HOLOGRAM_CHICKEN "Chicken"
#define AI_HOLOGRAM_CORGI "Corgi"
#define AI_HOLOGRAM_COW "Cow"
#define AI_HOLOGRAM_CRAB "Crab"
#define AI_HOLOGRAM_DEFAULT "Default"
#define AI_HOLOGRAM_FACE "Floating Face"
#define AI_HOLOGRAM_FOX "Fox"
#define AI_HOLOGRAM_GOAT "Goat"
#define AI_HOLOGRAM_NARSIE "Narsie"
#define AI_HOLOGRAM_PARROT "Parrot"
#define AI_HOLOGRAM_PUG "Pug"
#define AI_HOLOGRAM_RATVAR "Ratvar"
#define AI_HOLOGRAM_SPIDER "Spider"
#define AI_HOLOGRAM_XENO "Xeno Queen"
/// Icon state to use for ai displays that just turns them off
#define AI_DISPLAY_DONT_GLOW "ai_off"
/// Throw modes, defines whether or not to turn off throw mode after
-62
View File
@@ -122,68 +122,6 @@ GLOBAL_LIST_INIT(ghost_forms_with_accessories_list, list(
))
//stores the ghost forms that support hair and other such things
GLOBAL_LIST_INIT(ai_core_display_screens, sort_list(list(
":thinking:",
"Alien",
"Angel",
"Banned",
"Bliss",
"Blue",
"Clown",
"Database",
"Dorf",
"Firewall",
"Fuzzy",
"Gentoo",
"Glitchman",
"Gondola",
"Goon",
"Hades",
"HAL 9000",
"Heartline",
"Helios",
"House",
"Inverted",
"Matrix",
"Monochrome",
"Murica",
"Nanotrasen",
"Not Malf",
"Portrait",
"President",
"Rainbow",
"Random",
"Red October",
"Red",
"Static",
"Syndicat Meow",
"Text",
"Too Deep",
"Triumvirate-M",
"Triumvirate",
"Weird",
)))
/// A form of resolve_ai_icon that is guaranteed to never sleep.
/// Not always accurate, but always synchronous.
/proc/resolve_ai_icon_sync(input)
SHOULD_NOT_SLEEP(TRUE)
if(!input || !(input in GLOB.ai_core_display_screens))
return "ai"
else
if(input == "Random")
input = pick(GLOB.ai_core_display_screens - "Random")
return "ai-[lowertext(input)]"
/proc/resolve_ai_icon(input)
if (input == "Portrait")
var/datum/portrait_picker/tgui = new(usr)//create the datum
tgui.ui_interact(usr)//datum has a tgui component, here we open the window
return "ai-portrait" //just take this until they decide
return resolve_ai_icon_sync(input)
GLOBAL_LIST_INIT(security_depts_prefs, sort_list(list(
SEC_DEPT_ENGINEERING,
SEC_DEPT_MEDICAL,
+4 -26
View File
@@ -463,29 +463,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/status_display/evac, 32)
name = "\improper AI display"
desc = "A small screen which the AI can use to present itself."
current_mode = SD_PICTURE
var/emotion = AI_EMOTION_BLANK
/// A mapping between AI_EMOTION_* string constants, which also double as user readable descriptions, and the name of the iconfile.
var/static/list/emotion_map = list(
AI_EMOTION_BLANK = AI_DISPLAY_DONT_GLOW,
AI_EMOTION_VERY_HAPPY = "ai_veryhappy",
AI_EMOTION_HAPPY = "ai_happy",
AI_EMOTION_NEUTRAL = "ai_neutral",
AI_EMOTION_UNSURE = "ai_unsure",
AI_EMOTION_CONFUSED = "ai_confused",
AI_EMOTION_SAD = "ai_sad",
AI_EMOTION_BSOD = "ai_bsod",
AI_EMOTION_PROBLEMS = "ai_trollface",
AI_EMOTION_AWESOME = "ai_awesome",
AI_EMOTION_DORFY = "ai_urist",
AI_EMOTION_THINKING = "ai_thinking",
AI_EMOTION_FACEPALM = "ai_facepalm",
AI_EMOTION_FRIEND_COMPUTER = "ai_friend",
AI_EMOTION_BLUE_GLOW = "ai_sal",
AI_EMOTION_RED_GLOW = "ai_hal",
)
var/emotion = AI_DISPLAY_DONT_GLOW
MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/status_display/ai, 32)
@@ -501,8 +479,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/status_display/ai, 32)
if(!isAI(user))
return
var/list/choices = list()
for(var/emotion_const in emotion_map)
var/icon_state = emotion_map[emotion_const]
for(var/emotion_const in GLOB.ai_status_display_emotes)
var/icon_state = GLOB.ai_status_display_emotes[emotion_const]
choices[emotion_const] = image(icon = 'icons/obj/status_display.dmi', icon_state = icon_state)
var/emotion_result = show_radial_menu(user, src, choices, tooltips = TRUE)
@@ -517,7 +495,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/status_display/ai, 32)
update_appearance()
return PROCESS_KILL
set_picture(emotion_map[emotion])
set_picture(GLOB.ai_status_display_emotes[emotion])
return PROCESS_KILL
/obj/item/circuit_component/status_display
@@ -8,7 +8,7 @@
/datum/preference/choiced/ai_core_display/init_possible_values()
var/list/values = list()
values["Random"] = icon('icons/mob/silicon/ai.dmi', "ai-empty")
values["Random"] = icon('icons/mob/silicon/ai.dmi', "questionmark")
for (var/screen in GLOB.ai_core_display_screens - "Portrait" - "Random")
values[screen] = icon('icons/mob/silicon/ai.dmi', resolve_ai_icon_sync(screen))
@@ -0,0 +1,26 @@
/// What to show on the AI monitor
/datum/preference/choiced/ai_emote_display
category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "preferred_ai_emote_display"
should_generate_icons = TRUE
/datum/preference/choiced/ai_emote_display/init_possible_values()
var/list/values = list()
values["Random"] = icon('icons/mob/silicon/ai.dmi', "questionmark")
for(var/emote in GLOB.ai_status_display_emotes)
var/emote_icon = GLOB.ai_status_display_emotes[emote]
values[emote] = icon('icons/obj/status_display.dmi', emote_icon)
return values
/datum/preference/choiced/ai_emote_display/is_accessible(datum/preferences/preferences)
if (!..(preferences))
return FALSE
return istype(preferences.get_highest_priority_job(), /datum/job/ai)
/datum/preference/choiced/ai_emote_display/apply_to_human(mob/living/carbon/human/target, value)
return
@@ -0,0 +1,25 @@
/// What to show on the AI hologram
/datum/preference/choiced/ai_hologram_display
category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "preferred_ai_hologram_display"
should_generate_icons = TRUE
/datum/preference/choiced/ai_hologram_display/init_possible_values()
var/list/values = list()
values["Random"] = icon('icons/mob/silicon/ai.dmi', "questionmark")
for(var/hologram in GLOB.ai_hologram_icons - "Random")
values[hologram] = icon(GLOB.ai_hologram_icons[hologram], GLOB.ai_hologram_icon_state[hologram])
return values
/datum/preference/choiced/ai_hologram_display/is_accessible(datum/preferences/preferences)
if (!..(preferences))
return FALSE
return istype(preferences.get_highest_priority_job(), /datum/job/ai)
/datum/preference/choiced/ai_hologram_display/apply_to_human(mob/living/carbon/human/target, value)
return
+2 -1
View File
@@ -506,7 +506,8 @@
return
apply_pref_name(/datum/preference/name/ai, player_client) // This proc already checks if the player is appearance banned.
set_core_display_icon(null, player_client)
apply_pref_emote_display(player_client)
apply_pref_hologram_display(player_client)
/mob/living/silicon/robot/apply_prefs_job(client/player_client, datum/job/job)
if(mmi)
@@ -0,0 +1,126 @@
// A mapping between AI_EMOTION_* string constants, which also double as user readable descriptions, and the name of the iconfile. (used for /obj/machinery/status_display/ai )
GLOBAL_LIST_INIT(ai_status_display_emotes, list(
AI_EMOTION_AWESOME = "ai_awesome",
AI_EMOTION_BLANK = AI_DISPLAY_DONT_GLOW,
AI_EMOTION_BLUE_GLOW = "ai_sal",
AI_EMOTION_BSOD = "ai_bsod",
AI_EMOTION_CONFUSED = "ai_confused",
AI_EMOTION_DORFY = "ai_urist",
AI_EMOTION_FACEPALM = "ai_facepalm",
AI_EMOTION_FRIEND_COMPUTER = "ai_friend",
AI_EMOTION_HAPPY = "ai_happy",
AI_EMOTION_NEUTRAL = "ai_neutral",
AI_EMOTION_PROBLEMS = "ai_trollface",
AI_EMOTION_RED_GLOW = "ai_hal",
AI_EMOTION_SAD = "ai_sad",
AI_EMOTION_THINKING = "ai_thinking",
AI_EMOTION_UNSURE = "ai_unsure",
AI_EMOTION_VERY_HAPPY = "ai_veryhappy",
))
// New items need to also be added to ai_hologram_icon_state list
GLOBAL_LIST_INIT(ai_hologram_icons, list(
AI_HOLOGRAM_BEAR = 'icons/mob/simple/animal.dmi',
AI_HOLOGRAM_CARP = 'icons/mob/simple/carp.dmi',
AI_HOLOGRAM_CAT = 'icons/mob/simple/pets.dmi',
AI_HOLOGRAM_CAT_2 = 'icons/mob/simple/pets.dmi',
AI_HOLOGRAM_CHICKEN = 'icons/mob/simple/animal.dmi',
AI_HOLOGRAM_CORGI = 'icons/mob/simple/pets.dmi',
AI_HOLOGRAM_COW = 'icons/mob/simple/animal.dmi',
AI_HOLOGRAM_CRAB = 'icons/mob/simple/animal.dmi',
AI_HOLOGRAM_DEFAULT = 'icons/mob/silicon/ai.dmi',
AI_HOLOGRAM_FACE = 'icons/mob/silicon/ai.dmi',
AI_HOLOGRAM_FOX = 'icons/mob/simple/pets.dmi',
AI_HOLOGRAM_GOAT = 'icons/mob/simple/animal.dmi',
AI_HOLOGRAM_NARSIE = 'icons/mob/silicon/ai.dmi',
AI_HOLOGRAM_PARROT = 'icons/mob/simple/animal.dmi',
AI_HOLOGRAM_PUG = 'icons/mob/simple/pets.dmi',
AI_HOLOGRAM_RATVAR = 'icons/mob/silicon/ai.dmi',
AI_HOLOGRAM_SPIDER = 'icons/mob/simple/animal.dmi',
AI_HOLOGRAM_XENO = 'icons/mob/nonhuman-player/alien.dmi',
))
// New items need to also be added to ai_hologram_icons list
GLOBAL_LIST_INIT(ai_hologram_icon_state, list(
AI_HOLOGRAM_BEAR = "bear",
AI_HOLOGRAM_CARP = "carp",
AI_HOLOGRAM_CAT = "cat",
AI_HOLOGRAM_CAT_2 = "cat2",
AI_HOLOGRAM_CHICKEN = "chicken_brown",
AI_HOLOGRAM_CORGI = "corgi",
AI_HOLOGRAM_COW = "cow",
AI_HOLOGRAM_CRAB = "crab",
AI_HOLOGRAM_DEFAULT = "default",
AI_HOLOGRAM_FACE = "floating face",
AI_HOLOGRAM_FOX = "fox",
AI_HOLOGRAM_GOAT = "goat",
AI_HOLOGRAM_NARSIE = "horror",
AI_HOLOGRAM_PARROT = "parrot_fly",
AI_HOLOGRAM_PUG = "pug",
AI_HOLOGRAM_RATVAR = "clock",
AI_HOLOGRAM_SPIDER = "guard",
AI_HOLOGRAM_XENO = "alienq",
))
GLOBAL_LIST_INIT(ai_core_display_screens, sort_list(list(
":thinking:",
"Alien",
"Angel",
"Banned",
"Bliss",
"Blue",
"Clown",
"Database",
"Dorf",
"Firewall",
"Fuzzy",
"Gentoo",
"Glitchman",
"Gondola",
"Goon",
"Hades",
"HAL 9000",
"Heartline",
"Helios",
"House",
"Inverted",
"Matrix",
"Monochrome",
"Murica",
"Nanotrasen",
"Not Malf",
"Portrait",
"President",
"Rainbow",
"Random",
"Red October",
"Red",
"Static",
"Syndicat Meow",
"Text",
"Too Deep",
"Triumvirate-M",
"Triumvirate",
"Weird",
)))
/// A form of resolve_ai_icon that is guaranteed to never sleep.
/// Not always accurate, but always synchronous.
/proc/resolve_ai_icon_sync(input)
SHOULD_NOT_SLEEP(TRUE)
if(!input || !(input in GLOB.ai_core_display_screens))
return "ai"
else
if(input == "Random")
input = pick(GLOB.ai_core_display_screens - "Random")
return "ai-[lowertext(input)]"
/proc/resolve_ai_icon(input)
if (input == "Portrait")
var/datum/portrait_picker/tgui = new(usr)//create the datum
tgui.ui_interact(usr)//datum has a tgui component, here we open the window
return "ai-portrait" //just take this until they decide
return resolve_ai_icon_sync(input)
+30 -4
View File
@@ -157,21 +157,20 @@
create_modularInterface()
// /mob/living/silicon/ai/apply_prefs_job() uses these to set these procs at mapload
// this is used when a person is being inserted into an AI core during a round
if(client)
INVOKE_ASYNC(src, PROC_REF(apply_pref_name), /datum/preference/name/ai, client)
INVOKE_ASYNC(src, PROC_REF(apply_pref_hologram_display), client)
INVOKE_ASYNC(src, PROC_REF(set_core_display_icon))
hologram_appearance = mutable_appearance('icons/mob/silicon/ai.dmi',"default")
spark_system = new /datum/effect_system/spark_spread()
spark_system.set_up(5, 0, src)
spark_system.attach(src)
add_verb(src, /mob/living/silicon/ai/proc/show_laws_verb)
aiMulti = new(src)
aicamera = new/obj/item/camera/siliconcam/ai_camera(src)
@@ -262,6 +261,33 @@
var/preferred_icon = input ? input : C.prefs.read_preference(/datum/preference/choiced/ai_core_display)
icon_state = resolve_ai_icon(preferred_icon)
/// Apply an AI's hologram preference
/mob/living/silicon/ai/proc/apply_pref_hologram_display(client/player_client)
if(player_client.prefs?.read_preference(/datum/preference/choiced/ai_hologram_display))
var/list/hologram_choice = player_client.prefs.read_preference(/datum/preference/choiced/ai_hologram_display)
if(hologram_choice == "Random")
hologram_choice = pick(GLOB.ai_hologram_icons)
hologram_appearance = mutable_appearance(GLOB.ai_hologram_icons[hologram_choice], GLOB.ai_hologram_icon_state[hologram_choice])
hologram_appearance ||= mutable_appearance(GLOB.ai_hologram_icons[AI_HOLOGRAM_DEFAULT], GLOB.ai_hologram_icon_state[AI_HOLOGRAM_DEFAULT])
/// Apply an AI's emote display preference
/mob/living/silicon/ai/proc/apply_pref_emote_display(client/player_client)
if(player_client.prefs?.read_preference(/datum/preference/choiced/ai_emote_display))
var/emote_choice = player_client.prefs.read_preference(/datum/preference/choiced/ai_emote_display)
if(emote_choice == "Random")
emote_choice = pick(GLOB.ai_status_display_emotes)
apply_emote_display(emote_choice)
/// Apply an emote to all AI status displays on the station
/mob/living/silicon/ai/proc/apply_emote_display(emote)
for(var/obj/machinery/status_display/ai/ai_display as anything in GLOB.ai_status_displays)
ai_display.emotion = emote
ai_display.update()
/mob/living/silicon/ai/verb/pick_icon()
set category = "AI Commands"
set name = "Set AI Core Display"
+4 -12
View File
@@ -7,23 +7,15 @@
key = "blank"
var/emotion = AI_EMOTION_BLANK
/datum/emote/ai/emotion_display/run_emote(mob/user, params, type_override, intentional)
/datum/emote/ai/emotion_display/run_emote(mob/living/silicon/ai/user, params, type_override, intentional)
. = ..()
if(!.)
return
var/mob/living/silicon/ai/ai = user
var/turf/ai_turf = get_turf(ai)
for(var/obj/machinery/status_display/ai/ai_display as anything in GLOB.ai_status_displays)
var/turf/display_turf = get_turf(ai_display)
if(!istype(user))
return
// - Station AIs can change every display on the station Z.
// - Ghost role AIs (or AIs on the mining base?) can only affect their Z
if(!is_valid_z_level(ai_turf, display_turf))
continue
ai_display.emotion = emotion
ai_display.update()
user.apply_emote_display(emotion)
/datum/emote/ai/emotion_display/very_happy
key = "veryhappy"
+2
View File
@@ -99,6 +99,8 @@
if(preference_source)
apply_pref_name(/datum/preference/name/ai, preference_source)
our_AI.apply_pref_hologram_display(preference_source)
our_AI.set_core_display_icon(null, preference_source)
qdel(src)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 KiB

After

Width:  |  Height:  |  Size: 410 KiB

+1
View File
@@ -141,6 +141,7 @@ As an Engineer, your departmental protolathe and circuit printer can manufacture
As an Engineer, your RCD can be reloaded with iron, glass or plasteel sheets instead of just compressed matter cartridges.
As an Engineering Cyborg, you can attach air alarm/fire alarm/APC frames to walls by placing them on the floor and using a screwdriver on them.
As the AI, you can click on people's names to look at them. This only works if there are cameras that can see them, they aren't wearing agent IDs and aren't using digital camouflage as changelings.
As the AI, you can emote to change the status displays near your core. Some emotes can even be used to change the status displays across the entire station.
As the AI, you can quickly open and close doors by holding shift while clicking them, bolt them when holding ctrl, and even shock them while holding alt.
As the AI, you can take pictures with your camera and upload them to newscasters.
As the AI, you can use CTRL + 1-9 to set a location hotkey for your camera, allowing you to save the location and jump to it at will. Tilde and zero will return you to the last spot you jumped from, and the numpad numbers act as aliases to the regular number keys.
+3
View File
@@ -3054,6 +3054,8 @@
#include "code\modules\client\preferences\admin.dm"
#include "code\modules\client\preferences\age.dm"
#include "code\modules\client\preferences\ai_core_display.dm"
#include "code\modules\client\preferences\ai_emote_display.dm"
#include "code\modules\client\preferences\ai_hologram_display.dm"
#include "code\modules\client\preferences\ambient_occlusion.dm"
#include "code\modules\client\preferences\assets.dm"
#include "code\modules\client\preferences\auto_fit_viewport.dm"
@@ -4150,6 +4152,7 @@
#include "code\modules\mob\living\silicon\silicon_defense.dm"
#include "code\modules\mob\living\silicon\silicon_movement.dm"
#include "code\modules\mob\living\silicon\silicon_say.dm"
#include "code\modules\mob\living\silicon\ai\_preferences.dm"
#include "code\modules\mob\living\silicon\ai\ai.dm"
#include "code\modules\mob\living\silicon\ai\ai_defense.dm"
#include "code\modules\mob\living\silicon\ai\ai_portrait_picker.dm"
@@ -0,0 +1,6 @@
import { FeatureIconnedDropdownInput, FeatureWithIcons } from '../base';
export const preferred_ai_emote_display: FeatureWithIcons<string> = {
name: 'AI emote display',
component: FeatureIconnedDropdownInput,
};
@@ -0,0 +1,6 @@
import { FeatureIconnedDropdownInput, FeatureWithIcons } from '../base';
export const preferred_ai_hologram_display: FeatureWithIcons<string> = {
name: 'AI hologram display',
component: FeatureIconnedDropdownInput,
};