diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm index 520b98d39ad..3357116d308 100644 --- a/code/_helpers/text.dm +++ b/code/_helpers/text.dm @@ -620,3 +620,8 @@ return json_decode(data) catch return null + +/// Removes all non-alphanumerics from the text, keep in mind this can lead to id conflicts +/proc/sanitize_css_class_name(name) + var/static/regex/regex = new(@"[^a-zA-Z0-9]","g") + return replacetext(name, regex, "") diff --git a/code/game/objects/items/antag_spawners.dm b/code/game/objects/items/antag_spawners.dm index 8371f5b0175..28817531895 100644 --- a/code/game/objects/items/antag_spawners.dm +++ b/code/game/objects/items/antag_spawners.dm @@ -118,7 +118,6 @@ to speak with your team, and learn what their plan is for today.")) R.key = C.key -// R.Namepick() // Apparnetly making someone a merc lets them pick a name, so this isn't needed. spawn(1) mercs.add_antagonist(R.mind, FALSE, TRUE, FALSE, FALSE, FALSE) diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm index 32af47b31af..2667b9d3f66 100644 --- a/code/game/objects/items/robot/robot_parts.dm +++ b/code/game/objects/items/robot/robot_parts.dm @@ -225,7 +225,6 @@ feedback_inc("cyborg_birth",1) callHook("borgify", list(O)) - O.namepick() qdel(src) else diff --git a/code/game/objects/structures/ghost_pods/silicon.dm b/code/game/objects/structures/ghost_pods/silicon.dm index 64ac8f5674b..4f50784aed2 100644 --- a/code/game/objects/structures/ghost_pods/silicon.dm +++ b/code/game/objects/structures/ghost_pods/silicon.dm @@ -31,7 +31,6 @@ from the pod is not a crewmember.")) R.ckey = M.ckey visible_message(span_warning("As \the [src] opens, the eyes of the robot flicker as it is activated.")) - R.namepick() log_and_message_admins("successfully opened \a [src] and got a Lost Drone.") ..() @@ -57,7 +56,6 @@ definiton of 'your gravesite' is where your pod is.")) R.ckey = M.ckey visible_message(span_warning("As \the [src] opens, the eyes of the robot flicker as it is activated.")) - R.namepick() ..() /obj/structure/ghost_pod/ghost_activated/swarm_drone diff --git a/code/game/objects/structures/ghost_pods/silicon_vr.dm b/code/game/objects/structures/ghost_pods/silicon_vr.dm index cfc3b781d2f..1039349f2e1 100644 --- a/code/game/objects/structures/ghost_pods/silicon_vr.dm +++ b/code/game/objects/structures/ghost_pods/silicon_vr.dm @@ -21,7 +21,6 @@ from the pod is not a crewmember.")) R.ckey = M.ckey visible_message(span_warning("As \the [src] opens, the eyes of the robot flicker as it is activated.")) - R.namepick() log_and_message_admins("successfully opened \a [src] and got a Lost Drone.") used = TRUE return TRUE diff --git a/code/modules/admin/modify_robot.dm b/code/modules/admin/modify_robot.dm index f2847943f35..64f486824d7 100644 --- a/code/modules/admin/modify_robot.dm +++ b/code/modules/admin/modify_robot.dm @@ -43,9 +43,16 @@ qdel(source) . = ..() +/datum/eventkit/modify_robot/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/robot_icons) + ) + /datum/eventkit/modify_robot/tgui_data(mob/user) . = list() // Target section for general data + var/datum/asset/spritesheet/robot_icons/spritesheet = get_asset_datum(/datum/asset/spritesheet/robot_icons) + if(target) .["target"] = list() .["target"]["name"] = target.name @@ -62,10 +69,8 @@ // Target section for options once a module has been selected if(target.module) .["target"]["active"] = target.icon_selected - .["target"]["front"] = icon2base64(get_flat_icon(target,dir=SOUTH,no_anim=TRUE)) - .["target"]["side"] = icon2base64(get_flat_icon(target,dir=WEST,no_anim=TRUE)) - .["target"]["side_alt"] = icon2base64(get_flat_icon(target,dir=EAST,no_anim=TRUE)) - .["target"]["back"] = icon2base64(get_flat_icon(target,dir=NORTH,no_anim=TRUE)) + .["target"]["sprite"] = sanitize_css_class_name("[target.sprite_datum.type]") + .["target"]["sprite_size"] = spritesheet.icon_size_id(.["target"]["sprite"] + "S") .["target"]["modules"] = get_target_items(user) var/list/module_options = list() for(var/module in robot_modules) @@ -110,7 +115,7 @@ .["access_options"] = access_options // Section for source data for the module we might want to salvage if(source) - .["source"] += get_module_source(user) + .["source"] += get_module_source(user, spritesheet) var/list/all_robots = list() for(var/mob/living/silicon/robot/R in silicon_mob_list) if(!R.loc) @@ -573,10 +578,11 @@ target_items += list(list("name" = item.name, "ref" = "\ref[item]", "icon" = icon2html(item, user, sourceonly=TRUE), "desc" = item.desc)) return target_items -/datum/eventkit/modify_robot/proc/get_module_source(var/mob/user) +/datum/eventkit/modify_robot/proc/get_module_source(var/mob/user, var/datum/asset/spritesheet/robot_icons/spritesheet) var/list/source_list = list() source_list["model"] = source.module - source_list["front"] = icon2base64(get_flat_icon(source,dir=SOUTH,no_anim=TRUE)) + source_list["sprite"] = sanitize_css_class_name("[source.sprite_datum.type]") + source_list["sprite_size"] = spritesheet.icon_size_id(source_list["sprite"] + "S") var/list/source_items = list() for(var/obj/item in (source.module.modules | source.module.emag)) var/exists diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm index 572c1a5d26e..564b0879488 100644 --- a/code/modules/asset_cache/asset_list_items.dm +++ b/code/modules/asset_cache/asset_list_items.dm @@ -437,6 +437,45 @@ Insert("bottle-[i]", 'icons/obj/chemical.dmi', "bottle-[i]") +// Robot UI sprites +/datum/asset/spritesheet/robot_icons + name = "robot_icons" + +/datum/asset/spritesheet/robot_icons/create_spritesheets() + for(var/datum/robot_sprite/S as anything in typesof(/datum/robot_sprite)) + if(!S.name || !S.sprite_icon_state) // snowflake out those customs... they suck + continue + var/icon/I_N = icon(S.sprite_icon, S.sprite_icon_state, NORTH) + var/icon/I_S = icon(S.sprite_icon, S.sprite_icon_state, SOUTH) + var/icon/I_W = icon(S.sprite_icon, S.sprite_icon_state, WEST) + var/icon/I_E = icon(S.sprite_icon, S.sprite_icon_state, EAST) + if(S.has_eye_sprites) + var/icon/I_NE = icon(S.sprite_icon, "[S.sprite_icon_state]-eyes", NORTH) + if(I_NE) + I_N.Blend(I_NE, ICON_OVERLAY) + if(S.has_eye_sprites) + var/icon/I_SE = icon(S.sprite_icon, "[S.sprite_icon_state]-eyes", SOUTH) + if(I_SE) + I_S.Blend(I_SE, ICON_OVERLAY) + if(S.has_eye_sprites) + var/icon/I_WE = icon(S.sprite_icon, "[S.sprite_icon_state]-eyes", WEST) + if(I_WE) + I_W.Blend(I_WE, ICON_OVERLAY) + if(S.has_eye_sprites) + var/icon/I_EE = icon(S.sprite_icon, "[S.sprite_icon_state]-eyes", EAST) + if(I_EE) + I_E.Blend(I_EE, ICON_OVERLAY) + var/imgid = sanitize_css_class_name("[S.type]") + I_N.Scale(120, 120) + I_S.Scale(120, 120) + I_W.Scale(120, 120) + I_E.Scale(120, 120) + Insert(imgid + "N", I_N) + Insert(imgid + "S", I_S) + Insert(imgid + "W", I_W) + Insert(imgid + "E", I_E) + + //Cloning pod sprites for UIs /datum/asset/simple/cloning assets = list( diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index fd1132d05a8..c7c1a1a2fbc 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -203,9 +203,6 @@ var/list/mob_hat_cache = list() can_pick_shell = FALSE update_icon() -/mob/living/silicon/robot/drone/choose_icon() - return - /mob/living/silicon/robot/drone/pick_module() return diff --git a/code/modules/mob/living/silicon/robot/login.dm b/code/modules/mob/living/silicon/robot/login.dm index 89a136223e2..ef25b1bece0 100644 --- a/code/modules/mob/living/silicon/robot/login.dm +++ b/code/modules/mob/living/silicon/robot/login.dm @@ -11,11 +11,6 @@ repick_laws() // Forces synths to select an icon relevant to their module - if(!icon_selected) - icon_selection_tries = SSrobot_sprites.get_module_sprites_len(modtype, src) + 1 - choose_icon(icon_selection_tries) - - if(sprite_datum && module) - sprite_datum.do_equipment_glamour(module) + pick_module() plane_holder.set_vis(VIS_AUGMENTED, TRUE) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index a79310c02be..f3161e0e090 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -31,8 +31,7 @@ //Icon stuff var/datum/robot_sprite/sprite_datum // Sprite datum, holding all our sprite data - var/icon_selected = 1 // If icon selection has been completed yet - var/icon_selection_tries = 0 // Remaining attempts to select icon before a selection is forced + var/icon_selected = FALSE // If icon selection has been completed yet var/list/sprite_extra_customization = list() var/rest_style = "Default" var/notransform @@ -47,6 +46,9 @@ var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not var/obj/screen/robot_modules_background + var/ui_theme + var/selecting_module = FALSE + //3 Modules can be activated at any one time. var/obj/item/robot_module/module = null var/module_active = null @@ -336,46 +338,17 @@ return module_sprites */ /mob/living/silicon/robot/proc/pick_module() + if(icon_selected) + return if(module) - return - var/list/modules = list() - //VOREStatation Edit Start: shell restrictions - if(shell) - if(restrict_modules_to.len > 0) - modules.Add(restrict_modules_to) - else - modules.Add(shell_module_types) - else - if(restrict_modules_to.len > 0) - modules.Add(restrict_modules_to) - else - modules.Add(robot_module_types) - if(crisis || security_level == SEC_LEVEL_RED || crisis_override) - to_chat(src, span_red("Crisis mode active. Combat module available.")) - modules |= emergency_module_types - for(var/module_name in whitelisted_module_types) - if(is_borg_whitelisted(src, module_name)) - modules |= module_name - //VOREStatation Edit End: shell restrictions - modtype = tgui_input_list(usr, "Please, select a module!", "Robot module", modules) - - if(module) - return - if(!(modtype in robot_modules)) - return - if(!is_borg_whitelisted(src, modtype)) - return - - var/module_type = robot_modules[modtype] - transform_with_anim() //VOREStation edit: sprite animation - new module_type(src) - - hands.icon_state = get_hud_module_icon() - feedback_inc("cyborg_[lowertext(modtype)]",1) - updatename() - hud_used.update_robot_modules_display() - notify_ai(ROBOT_NOTIFICATION_NEW_MODULE, module.name) - robotact?.update_static_data_for_all_viewers() + var/list/module_sprites = SSrobot_sprites.get_module_sprites(module, src) + if(module_sprites.len == 1 || !client) + sprite_datum = module_sprites[1] + sprite_datum.do_equipment_glamour(module) + return + if(!selecting_module) + var/datum/tgui_module/robot_ui_module/ui = new(src) + ui.tgui_interact(src) /mob/living/silicon/robot/proc/update_braintype() if(istype(mmi, /obj/item/mmi/digital/posibrain)) @@ -436,15 +409,12 @@ to_chat(usr, "You can't pick another custom name. [isshell(src) ? "" : "Go ask for a name change."]") return 0 - spawn(0) - var/newname - newname = sanitizeSafe(tgui_input_text(src,"You are a robot. Enter a name, or leave blank for the default name.", "Name change","", MAX_NAME_LEN), MAX_NAME_LEN) - if (newname) - custom_name = newname - sprite_name = newname + var/newname = sanitizeSafe(tgui_input_text(src,"You are a robot. Enter a name, or leave blank for the default name.", "Name change","", MAX_NAME_LEN), MAX_NAME_LEN) + if (newname) + custom_name = newname + sprite_name = newname - updatename() - update_icon() + updatename() /mob/living/silicon/robot/verb/extra_customization() set name = "Customize Appearance" @@ -796,6 +766,7 @@ notify_ai(ROBOT_NOTIFICATION_MODULE_RESET, module.name) module.Reset(src) qdel(module) + icon_selected = FALSE module = null updatename("Default") has_recoloured = FALSE @@ -1088,60 +1059,6 @@ return -/mob/living/silicon/robot/proc/choose_icon(var/triesleft) - var/robot_species = null - if(!SSrobot_sprites) - to_chat(src, "Robot Sprites have not been initialized yet. How are you choosing a sprite? Harass a coder.") - return - - var/list/module_sprites = SSrobot_sprites.get_module_sprites(modtype, src) - if(!module_sprites || !module_sprites.len) - to_chat(src, "Your module appears to have no sprite options. Harass a coder.") - return - - icon_selected = 0 - icon_selection_tries = triesleft - if(module_sprites.len == 1 || !client) - if(!(sprite_datum in module_sprites)) - sprite_datum = module_sprites[1] - else - var/selection = tgui_input_list(src, "Select an icon! [triesleft ? "You have [triesleft] more chance\s." : "This is your last try."]", "Robot Icon", module_sprites) - if(selection) - sprite_datum = selection - else - sprite_datum = module_sprites[1] - if(!istype(src,/mob/living/silicon/robot/drone)) - robot_species = sprite_datum.name - if(notransform) - to_chat(src, "Your current transformation has not finished yet!") - choose_icon(icon_selection_tries) - return - else - transform_with_anim() - - var/tempheight = vis_height - update_icon() - // This is bad but I dunno other way to 'reset' our resize offset based on vis_height changes other than resizing to normal and back. - if(tempheight != vis_height) - var/tempsize = size_multiplier - resize(1) - resize(tempsize) - - - if (module_sprites.len > 1 && triesleft >= 1 && client) - icon_selection_tries-- - var/choice = tgui_alert(usr, "Look at your icon - is this what you want?", "Icon Choice", list("Yes","No")) - if(choice == "No") - choose_icon(icon_selection_tries) - return - - icon_selected = 1 - icon_selection_tries = 0 - sprite_type = robot_species - if(hands) - update_hud() - to_chat(src, span_filter_notice("Your icon has been set. You now require a module reset to change it.")) - /mob/living/silicon/robot/proc/set_default_module_icon() if(!SSrobot_sprites) return diff --git a/code/modules/mob/living/silicon/robot/robot_modules/station.dm b/code/modules/mob/living/silicon/robot/robot_modules/station.dm index 8f8c0757e0c..bc54a72e638 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/station.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/station.dm @@ -73,7 +73,7 @@ var/global/list/robot_modules = list( R.radio.recalculateChannels() R.set_default_module_icon() - R.choose_icon(SSrobot_sprites.get_module_sprites_len(R.modtype, R) + 1) + R.pick_module() if(!R.client) R.icon_selected = FALSE // It wasnt a player selecting icon? Let them do it later! @@ -93,7 +93,7 @@ var/global/list/robot_modules = list( if(R.radio) R.radio.recalculateChannels() - R.choose_icon(0) + R.set_default_module_icon() R.scrubbing = FALSE diff --git a/code/modules/mob/living/silicon/robot/robot_ui.dm b/code/modules/mob/living/silicon/robot/robot_ui.dm index 7262f45364a..11027cc20da 100644 --- a/code/modules/mob/living/silicon/robot/robot_ui.dm +++ b/code/modules/mob/living/silicon/robot/robot_ui.dm @@ -52,6 +52,10 @@ var/mob/living/silicon/robot/R = host data["module_name"] = R.module ? "[R.module]" : null + if(R.emagged) + data["theme"] = "syndicate" + else if (R.ui_theme) + data["theme"] = R.ui_theme if(!R.module) return data diff --git a/code/modules/mob/living/silicon/robot/robot_ui_module.dm b/code/modules/mob/living/silicon/robot/robot_ui_module.dm new file mode 100644 index 00000000000..25b199b47c0 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/robot_ui_module.dm @@ -0,0 +1,169 @@ +// Robot module selection +/datum/tgui_module/robot_ui_module + name = "Robot Module Configurator" + tgui_id = "RobotChoose" + var/selected_module + var/newName + var/datum/robot_sprite/sprite_datum + +/datum/tgui_module/robot_ui_module/tgui_state(mob/user) + return GLOB.tgui_self_state + +/datum/tgui_module/robot_ui_module/tgui_close(mob/user) + . = ..() + if(isrobot(user)) + var/mob/living/silicon/robot/R = user + R.selecting_module = FALSE + +/datum/tgui_module/robot_ui_module/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + . = ..() + if(isrobot(user) && ui) + var/mob/living/silicon/robot/R = user + R.selecting_module = TRUE + +/datum/tgui_module/robot_ui_module/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/robot_icons) + ) + +/datum/tgui_module/robot_ui_module/tgui_static_data() + var/list/data = ..() + + var/mob/living/silicon/robot/R = host + + var/list/modules = list() + if(R.module) + modules = list(R.modtype) + selected_module = R.modtype + else + if(LAZYLEN(R.restrict_modules_to) > 0) + modules.Add(R.restrict_modules_to) + else if(R.shell) + modules.Add(shell_module_types) + else + modules.Add(robot_module_types) + if(R.crisis || security_level == SEC_LEVEL_RED || R.crisis_override) + to_chat(R, span_red("Crisis mode active. Combat module available.")) + modules |= emergency_module_types + for(var/module_name in whitelisted_module_types) + if(is_borg_whitelisted(R, module_name)) + modules |= module_name + data["possible_modules"] = modules + if(R.emagged) + data["theme"] = "syndicate" + else if (R.ui_theme) + data["theme"] = R.ui_theme + + return data + +/datum/tgui_module/robot_ui_module/tgui_data() + var/list/data = ..() + + var/mob/living/silicon/robot/R = host + var/datum/asset/spritesheet/robot_icons/spritesheet = get_asset_datum(/datum/asset/spritesheet/robot_icons) + + data["currentName"] = newName ? newName : R.name + data["isDefaultName"] = !newName + + if(selected_module) + data["selected_module"] = selected_module + if(!SSrobot_sprites) + to_chat(R, span_warning("Robot Sprites have not been initialized yet. How are you choosing a sprite? Harass a coder.")) + selected_module = null + return + + var/list/module_sprites = SSrobot_sprites.get_module_sprites(selected_module, R) + if(!module_sprites || !module_sprites.len) + to_chat(R, span_warning("Your module appears to have no sprite options. Harass a coder.")) + selected_module = null + return + var/list/available_sprites = list() + for(var/datum/robot_sprite/S in module_sprites) + var/model_type = "def" + if(istype(S, /datum/robot_sprite/dogborg)) + model_type = "wide" + if(istype(S, /datum/robot_sprite/dogborg/tall)) + model_type = "tall" + available_sprites += list(list("sprite" = S.name, "belly" = S.has_vore_belly_sprites, "type" = model_type)) + + data["possible_sprites"] = available_sprites + data["sprite_datum"] = sprite_datum + data["sprite_datum_class"] = null + data["sprite_datum_size"] = null + if(sprite_datum) + data["sprite_datum_class"] = sanitize_css_class_name("[sprite_datum.type]") + data["sprite_datum_size"] = spritesheet.icon_size_id(data["sprite_datum_class"] + "S") // just get the south icon's size, the rest will be the same + + return data + +/datum/tgui_module/robot_ui_module/tgui_act(action, params) + . = ..() + if(.) + return + + var/mob/living/silicon/robot/R = host + + switch(action) + if("pick_module") + if(R.module) + return + var/new_module = params["value"] + if(!(new_module in robot_modules)) + return + if(!is_borg_whitelisted(R, new_module)) + return + selected_module = new_module + var/new_datum + var/list/module_sprites = SSrobot_sprites.get_module_sprites(selected_module, R) + for(var/datum/robot_sprite/S in module_sprites) + if(S.name == sprite_datum.name) + new_datum = S + break + sprite_datum = new_datum + . = TRUE + if("pick_icon") + var/sprite = params["value"] + if(!sprite) + return + var/list/module_sprites = SSrobot_sprites.get_module_sprites(selected_module, R) + for(var/datum/robot_sprite/S in module_sprites) + if(S.name == sprite) + sprite_datum = S + break + . = TRUE + if("rename") + var/name = params["value"] + if(name) + newName = sanitizeSafe(name, MAX_NAME_LEN) + . = TRUE + if("confirm") + if(!R.custom_name) + if (newName) + R.custom_name = newName + R.sprite_name = newName + R.icon_selected = TRUE + var/module_type = robot_modules[selected_module] + R.modtype = selected_module + R.module = new module_type(R) + feedback_inc("cyborg_[lowertext(selected_module)]",1) + R.updatename() + R.hud_used.update_robot_modules_display() + R.notify_ai(ROBOT_NOTIFICATION_NEW_MODULE, R.module.name) + R.robotact?.update_static_data_for_all_viewers() + R.sprite_datum = sprite_datum + if(!istype(R,/mob/living/silicon/robot/drone)) + R.sprite_type = sprite_datum.name + R.transform_with_anim() + var/tempheight = R.vis_height + R.update_icon() + // This is bad but I dunno other way to 'reset' our resize offset based on vis_height changes other than resizing to normal and back. + if(tempheight != R.vis_height) + var/tempsize = R.size_multiplier + R.resize(1) + R.resize(tempsize) + if(R.hands) + R.update_hud() + R.sprite_datum.do_equipment_glamour(R.module) + to_chat(R, span_filter_notice("Your icon has been set. You now require a module reset to change it.")) + close_ui() + . = TRUE diff --git a/code/modules/mob/living/silicon/robot/subtypes/gravekeeper.dm b/code/modules/mob/living/silicon/robot/subtypes/gravekeeper.dm index 73357c10711..ccf1908c166 100644 --- a/code/modules/mob/living/silicon/robot/subtypes/gravekeeper.dm +++ b/code/modules/mob/living/silicon/robot/subtypes/gravekeeper.dm @@ -9,6 +9,7 @@ icon_selected = FALSE can_be_antagged = FALSE restrict_modules_to = list("Gravekeeper") + ui_theme = "malfunction" /mob/living/silicon/robot/gravekeeper/init() aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) diff --git a/code/modules/mob/living/silicon/robot/subtypes/lost_drone.dm b/code/modules/mob/living/silicon/robot/subtypes/lost_drone.dm index 24e449c411c..9f0aa11446c 100644 --- a/code/modules/mob/living/silicon/robot/subtypes/lost_drone.dm +++ b/code/modules/mob/living/silicon/robot/subtypes/lost_drone.dm @@ -9,6 +9,7 @@ icon_selected = FALSE restrict_modules_to = list("Lost") var/law_retries = 5 + ui_theme = "malfunction" /mob/living/silicon/robot/lost/init() aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) diff --git a/code/modules/mob/living/silicon/robot/subtypes/syndicate.dm b/code/modules/mob/living/silicon/robot/subtypes/syndicate.dm index 5dd72332e44..15a8257a499 100644 --- a/code/modules/mob/living/silicon/robot/subtypes/syndicate.dm +++ b/code/modules/mob/living/silicon/robot/subtypes/syndicate.dm @@ -8,6 +8,7 @@ idcard_type = /obj/item/card/id/syndicate icon_selected = FALSE restrict_modules_to = list("Protector", "Mechanist", "Combat Medic") + ui_theme = "syndicate" /mob/living/silicon/robot/syndicate/init() aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index a686e35de7b..5d018586f1d 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -190,7 +190,6 @@ O.custom_speech_bubble = B.custom_speech_bubble callHook("borgify", list(O)) - O.namepick() spawn(0) // Mobs still instantly del themselves, thus we need to spawn or O will never be returned qdel(src) diff --git a/icons/mob/robot/janitor.dmi b/icons/mob/robot/janitor.dmi index 7410b353a75..3dbaa27a420 100644 Binary files a/icons/mob/robot/janitor.dmi and b/icons/mob/robot/janitor.dmi differ diff --git a/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotComponent.tsx b/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotComponent.tsx index 293db7da7de..165b95af2e7 100644 --- a/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotComponent.tsx +++ b/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotComponent.tsx @@ -2,16 +2,17 @@ import { capitalize } from 'common/string'; import { useState } from 'react'; import { useBackend } from 'tgui/backend'; import { + Box, Button, Divider, Flex, Icon, - Image, Input, Section, Stack, Tabs, } from 'tgui/components'; +import { classes } from 'tgui-core/react'; import { NoSpriteWarning } from '../components'; import { @@ -163,15 +164,18 @@ export const ModifyRobotComponent = (props: { - + + + + + + + diff --git a/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotModules.tsx b/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotModules.tsx index 045025ccd56..85ad5a6da4c 100644 --- a/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotModules.tsx +++ b/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotModules.tsx @@ -13,6 +13,7 @@ import { Section, Stack, } from 'tgui/components'; +import { classes } from 'tgui-core/react'; import { NoSpriteWarning } from '../components'; import { prepareSearch } from '../functions'; @@ -47,7 +48,8 @@ export const ModifyRobotModules = (props: { /> {!!source && ( - + + + + + + + { /> - + + + + + + + { /> - + + + + + + + { + const { act } = useBackend(); + const { currentName, isDefaultName, sprite, size } = props; + + return ( +
act('confirm')}> + Confirm + + } + > + + + + Name: + + + + act('rename', { value })} + maxLength={52} + textColor={isDefaultName ? 'red' : undefined} + /> + + + + + {!!sprite && !!size && ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/RobotChoose/ModuleSection.tsx b/tgui/packages/tgui/interfaces/RobotChoose/ModuleSection.tsx new file mode 100644 index 00000000000..c90dbbe7be5 --- /dev/null +++ b/tgui/packages/tgui/interfaces/RobotChoose/ModuleSection.tsx @@ -0,0 +1,47 @@ +import { createSearch } from 'common/string'; +import { useState } from 'react'; +import { Input, Section, Stack } from 'tgui-core/components'; + +import { SelectorElement } from './SelectorElement'; + +export const ModuleSection = (props: { + title: string; + sortable?: string[]; + selected?: string; +}) => { + const { title, sortable, selected } = props; + + const [searchText, setSearchText] = useState(''); + + const searcher = createSearch(searchText, (element: string) => { + return element; + }); + + const filtered = sortable?.filter(searcher); + + return ( +
+ + setSearchText(value)} + /> + + + + {!!filtered && + filtered.map((filter) => ( + + ))} + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/RobotChoose/SelectorElement.tsx b/tgui/packages/tgui/interfaces/RobotChoose/SelectorElement.tsx new file mode 100644 index 00000000000..3279ce78829 --- /dev/null +++ b/tgui/packages/tgui/interfaces/RobotChoose/SelectorElement.tsx @@ -0,0 +1,34 @@ +import { useBackend } from 'tgui/backend'; +import { Button, Flex, Icon, Stack } from 'tgui-core/components'; + +export const SelectorElement = (props: { + option: string; + action: string; + selected?: string | null; + belly?: boolean; +}) => { + const { act } = useBackend(); + const { option, action, selected, belly } = props; + + return ( + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/RobotChoose/SpriteSection.tsx b/tgui/packages/tgui/interfaces/RobotChoose/SpriteSection.tsx new file mode 100644 index 00000000000..d287792d025 --- /dev/null +++ b/tgui/packages/tgui/interfaces/RobotChoose/SpriteSection.tsx @@ -0,0 +1,80 @@ +import { useState } from 'react'; +import { Button, Input, Section, Stack } from 'tgui-core/components'; + +import { robotSpriteSearcher } from './functions'; +import { SelectorElement } from './SelectorElement'; +import { spriteOption } from './types'; + +export const SpriteSection = (props: { + title: string; + selected?: string | null; + sortable?: spriteOption[]; +}) => { + const { title, sortable, selected } = props; + + const [searchText, setSearchText] = useState(''); + const [includeDefault, setIncludeDefault] = useState(false); + const [includeWide, setInclideDog] = useState(false); + const [includeTall, setIncludeTall] = useState(false); + + const filtered = robotSpriteSearcher( + searchText, + includeDefault, + includeWide, + includeTall, + sortable, + ); + return ( +
+ setIncludeDefault(!includeDefault)} + > + Def + + setInclideDog(!includeWide)} + > + Wide + + setIncludeTall(!includeTall)} + > + Tall + + + } + > + + setSearchText(value)} + /> + + + + {!!filtered && + filtered.map((filter) => ( + + ))} + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/RobotChoose/functions.ts b/tgui/packages/tgui/interfaces/RobotChoose/functions.ts new file mode 100644 index 00000000000..db60717e88e --- /dev/null +++ b/tgui/packages/tgui/interfaces/RobotChoose/functions.ts @@ -0,0 +1,47 @@ +import { filter } from 'common/collections'; +import { flow } from 'common/fp'; +import { createSearch } from 'common/string'; + +import { spriteOption } from './types'; + +export function robotSpriteSearcher( + searchText: string, + includeDefault: boolean, + includeWide: boolean, + includeTall: boolean, + sprites?: spriteOption[], +): spriteOption[] { + const testSearch = createSearch( + searchText, + (sprite: spriteOption) => sprite.sprite, + ); + if (!sprites) { + return []; + } + let subtypes: string[] = []; + if (includeDefault) { + subtypes.push('def'); + } + if (includeWide) { + subtypes.push('wide'); + } + if (includeTall) { + subtypes.push('tall'); + } + return flow([ + (sprites: spriteOption[]) => { + if (!searchText) { + return sprites; + } else { + return filter(sprites, testSearch); + } + }, + (sprites: spriteOption[]) => { + if (!subtypes.length) { + return sprites; + } else { + return filter(sprites, (sprite) => subtypes.includes(sprite.type)); + } + }, + ])(sprites); +} diff --git a/tgui/packages/tgui/interfaces/RobotChoose/index.tsx b/tgui/packages/tgui/interfaces/RobotChoose/index.tsx new file mode 100644 index 00000000000..91d8dcb7d57 --- /dev/null +++ b/tgui/packages/tgui/interfaces/RobotChoose/index.tsx @@ -0,0 +1,49 @@ +import { useBackend } from 'tgui/backend'; +import { Window } from 'tgui/layouts'; +import { Stack } from 'tgui-core/components'; + +import { IconSection } from './IconSection'; +import { ModuleSection } from './ModuleSection'; +import { SpriteSection } from './SpriteSection'; +import { Data } from './types'; + +export const RobotChoose = (props) => { + const { data } = useBackend(); + + const { + possible_modules, + possible_sprites, + selected_module, + sprite_datum, + theme, + currentName, + isDefaultName, + sprite_datum_class, + sprite_datum_size, + } = data; + + return ( + + + + + + + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/RobotChoose/types.ts b/tgui/packages/tgui/interfaces/RobotChoose/types.ts new file mode 100644 index 00000000000..06d691c5893 --- /dev/null +++ b/tgui/packages/tgui/interfaces/RobotChoose/types.ts @@ -0,0 +1,13 @@ +export type Data = { + possible_modules: string[]; + possible_sprites?: spriteOption[]; + currentName: string; + isDefaultName: boolean; + theme?: string; + selected_module?: string; + sprite_datum?: string | null; + sprite_datum_class?: string | null; + sprite_datum_size?: string | null; +}; + +export type spriteOption = { sprite: string; belly: boolean; type: string }; diff --git a/tgui/packages/tgui/interfaces/Robotact/index.tsx b/tgui/packages/tgui/interfaces/Robotact/index.tsx index 94b97253342..1ab5ea486aa 100644 --- a/tgui/packages/tgui/interfaces/Robotact/index.tsx +++ b/tgui/packages/tgui/interfaces/Robotact/index.tsx @@ -18,7 +18,7 @@ export const Robotact = (props) => { } return ( - + {content} ); diff --git a/tgui/packages/tgui/interfaces/Robotact/types.ts b/tgui/packages/tgui/interfaces/Robotact/types.ts index bd33676d99d..b4232ff6e43 100644 --- a/tgui/packages/tgui/interfaces/Robotact/types.ts +++ b/tgui/packages/tgui/interfaces/Robotact/types.ts @@ -31,6 +31,7 @@ export type Data = { max_health: number; light_color: string; + theme?: string; // Modules modules_static: Module[]; diff --git a/vorestation.dme b/vorestation.dme index 5ef4cbce393..dbe63b1438f 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -3163,6 +3163,7 @@ #include "code\modules\mob\living\silicon\robot\robot_movement.dm" #include "code\modules\mob\living\silicon\robot\robot_remote_control.dm" #include "code\modules\mob\living\silicon\robot\robot_ui.dm" +#include "code\modules\mob\living\silicon\robot\robot_ui_module.dm" #include "code\modules\mob\living\silicon\robot\dogborg\dog_modules_vr.dm" #include "code\modules\mob\living\silicon\robot\dogborg\dog_sleeper_vr.dm" #include "code\modules\mob\living\silicon\robot\drone\drone.dm"