From 5f35d87e8d6ebd52345a2a67d777734396e098f4 Mon Sep 17 00:00:00 2001 From: LatteKat <56778689+jupyterkat@users.noreply.github.com> Date: Fri, 9 Jul 2021 20:57:32 +1000 Subject: [PATCH] ports tg's filter debug menu & minor filter refactor --- beestation.dme | 2 + code/__DEFINES/colors.dm | 2 + code/__DEFINES/vv.dm | 5 + code/__HELPERS/filters.dm | 319 ++++++++++++++++++ code/__HELPERS/unsorted.dm | 13 - code/_onclick/hud/plane_master.dm | 32 +- code/game/atoms.dm | 56 ++- code/modules/admin/holder2.dm | 2 + .../admin/view_variables/filterrific.dm | 99 ++++++ .../nukeop/equipment/borgchameleon.dm | 21 +- code/modules/cargo/supplypod.dm | 4 +- code/modules/client/client_procs.dm | 15 + tgui/docs/component-reference.md | 8 +- tgui/packages/common/math.js | 10 + tgui/packages/tgui/components/Dropdown.js | 13 +- tgui/packages/tgui/interfaces/Filteriffic.js | 307 +++++++++++++++++ tgui/public/tgui-common.bundle.js | 1 + tgui/public/tgui.bundle.js | 6 +- 18 files changed, 854 insertions(+), 61 deletions(-) create mode 100644 code/__HELPERS/filters.dm create mode 100644 code/modules/admin/view_variables/filterrific.dm create mode 100644 tgui/packages/tgui/interfaces/Filteriffic.js create mode 100644 tgui/public/tgui-common.bundle.js diff --git a/beestation.dme b/beestation.dme index 46a935d389a..e03aefb99f5 100644 --- a/beestation.dme +++ b/beestation.dme @@ -133,6 +133,7 @@ #include "code\__HELPERS\dates.dm" #include "code\__HELPERS\dna.dm" #include "code\__HELPERS\files.dm" +#include "code\__HELPERS\filters.dm" #include "code\__HELPERS\game.dm" #include "code\__HELPERS\global_lists.dm" #include "code\__HELPERS\heap.dm" @@ -1335,6 +1336,7 @@ #include "code\modules\admin\verbs\SDQL2\SDQL_2_wrappers.dm" #include "code\modules\admin\view_variables\admin_delete.dm" #include "code\modules\admin\view_variables\debug_variables.dm" +#include "code\modules\admin\view_variables\filterrific.dm" #include "code\modules\admin\view_variables\get_variables.dm" #include "code\modules\admin\view_variables\mark_datum.dm" #include "code\modules\admin\view_variables\mass_edit_variables.dm" diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index 676213e0921..4aa12e4f843 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -71,3 +71,5 @@ "#D6B20C",\ "#FF902A",\ ) + +#define COLOR_HALF_TRANSPARENT_BLACK "#0000007A" diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm index 81992dbe983..6b9f2d224d4 100644 --- a/code/__DEFINES/vv.dm +++ b/code/__DEFINES/vv.dm @@ -85,6 +85,11 @@ #define VV_HK_TRIGGER_EMP "empulse" #define VV_HK_TRIGGER_EXPLOSION "explode" #define VV_HK_AUTO_RENAME "auto_rename" +<<<<<<< HEAD +======= +#define VV_HK_EDIT_FILTERS "edit_filters" +#define VV_HK_ADD_AI "add_ai" +>>>>>>> 203156d3e2 (ports tg's filter debug menu & minor filter refactor (#4434)) // /obj #define VV_HK_OSAY "osay" diff --git a/code/__HELPERS/filters.dm b/code/__HELPERS/filters.dm new file mode 100644 index 00000000000..7be7ca5d732 --- /dev/null +++ b/code/__HELPERS/filters.dm @@ -0,0 +1,319 @@ +#define ICON_NOT_SET "Not Set" + +//This is stored as a nested list instead of datums or whatever because it json encodes nicely for usage in tgui +GLOBAL_LIST_INIT(master_filter_info, list( + "alpha" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "icon" = ICON_NOT_SET, + "render_source" = "", + "flags" = 0 + ), + "flags" = list( + "MASK_INVERSE" = MASK_INVERSE, + "MASK_SWAP" = MASK_SWAP + ) + ), + "angular_blur" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 1 + ) + ), + /* Not supported because making a proper matrix editor on the frontend would be a huge dick pain. + Uncomment if you ever implement it + "color" = list( + "defaults" = list( + "color" = matrix(), + "space" = FILTER_COLOR_RGB + ) + ), + */ + "displace" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = null, + "icon" = ICON_NOT_SET, + "render_source" = "" + ) + ), + "drop_shadow" = list( + "defaults" = list( + "x" = 1, + "y" = -1, + "size" = 1, + "offset" = 0, + "color" = COLOR_HALF_TRANSPARENT_BLACK + ) + ), + "blur" = list( + "defaults" = list( + "size" = 1 + ) + ), + "layer" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "icon" = ICON_NOT_SET, + "render_source" = "", + "flags" = FILTER_OVERLAY, + "color" = "", + "transform" = null, + "blend_mode" = BLEND_DEFAULT + ) + ), + "motion_blur" = list( + "defaults" = list( + "x" = 0, + "y" = 0 + ) + ), + "outline" = list( + "defaults" = list( + "size" = 0, + "color" = COLOR_BLACK, + "flags" = NONE + ), + "flags" = list( + "OUTLINE_SHARP" = OUTLINE_SHARP, + "OUTLINE_SQUARE" = OUTLINE_SQUARE + ) + ), + "radial_blur" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 0.01 + ) + ), + "rays" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 16, + "color" = COLOR_WHITE, + "offset" = 0, + "density" = 10, + "threshold" = 0.5, + "factor" = 0, + "flags" = FILTER_OVERLAY | FILTER_UNDERLAY + ), + "flags" = list( + "FILTER_OVERLAY" = FILTER_OVERLAY, + "FILTER_UNDERLAY" = FILTER_UNDERLAY + ) + ), + "ripple" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 1, + "repeat" = 2, + "radius" = 0, + "falloff" = 1, + "flags" = NONE + ), + "flags" = list( + "WAVE_BOUNDED" = WAVE_BOUNDED + ) + ), + "wave" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 1, + "offset" = 0, + "flags" = NONE + ), + "flags" = list( + "WAVE_SIDEWAYS" = WAVE_SIDEWAYS, + "WAVE_BOUNDED" = WAVE_BOUNDED + ) + ) +)) + +#undef ICON_NOT_SET + +//Helpers to generate lists for filter helpers +//This is the only practical way of writing these that actually produces sane lists +/proc/alpha_mask_filter(x, y, icon/icon, render_source, flags) + . = list("type" = "alpha") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(icon)) + .["icon"] = icon + if(!isnull(render_source)) + .["render_source"] = render_source + if(!isnull(flags)) + .["flags"] = flags + +/proc/angular_blur_filter(x, y, size) + . = list("type" = "angular_blur") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(size)) + .["size"] = size + +/proc/color_matrix_filter(matrix/in_matrix, space) + . = list("type" = "color") + .["color"] = in_matrix + if(!isnull(space)) + .["space"] = space + +/proc/displacement_map_filter(icon, render_source, x, y, size = 32) + . = list("type" = "displace") + if(!isnull(icon)) + .["icon"] = icon + if(!isnull(render_source)) + .["render_source"] = render_source + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(size)) + .["size"] = size + +/proc/drop_shadow_filter(x, y, size, offset, color) + . = list("type" = "drop_shadow") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(size)) + .["size"] = size + if(!isnull(offset)) + .["offset"] = offset + if(!isnull(color)) + .["color"] = color + +/proc/gauss_blur_filter(size) + . = list("type" = "blur") + if(!isnull(size)) + .["size"] = size + +/proc/layering_filter(icon, render_source, x, y, flags, color, transform, blend_mode) + . = list("type" = "layer") + if(!isnull(icon)) + .["icon"] = icon + if(!isnull(render_source)) + .["render_source"] = render_source + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(color)) + .["color"] = color + if(!isnull(flags)) + .["flags"] = flags + if(!isnull(transform)) + .["transform"] = transform + if(!isnull(blend_mode)) + .["blend_mode"] = blend_mode + +/proc/motion_blur_filter(x, y) + . = list("type" = "motion_blur") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + +/proc/outline_filter(size, color, flags) + . = list("type" = "outline") + if(!isnull(size)) + .["size"] = size + if(!isnull(color)) + .["color"] = color + if(!isnull(flags)) + .["flags"] = flags + +/proc/radial_blur_filter(size, x, y) + . = list("type" = "radial_blur") + if(!isnull(size)) + .["size"] = size + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + +/proc/rays_filter(size, color, offset, density, threshold, factor, x, y, flags) + . = list("type" = "rays") + if(!isnull(size)) + .["size"] = size + if(!isnull(color)) + .["color"] = color + if(!isnull(offset)) + .["offset"] = offset + if(!isnull(density)) + .["density"] = density + if(!isnull(threshold)) + .["threshold"] = threshold + if(!isnull(factor)) + .["factor"] = factor + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(flags)) + .["flags"] = flags + +/proc/ripple_filter(radius, size, falloff, repeat, x, y, flags) + . = list("type" = "ripple") + if(!isnull(radius)) + .["radius"] = radius + if(!isnull(size)) + .["size"] = size + if(!isnull(falloff)) + .["falloff"] = falloff + if(!isnull(repeat)) + .["repeat"] = repeat + if(!isnull(flags)) + .["flags"] = flags + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + +/proc/wave_filter(x, y, size, offset, flags) + . = list("type" = "wave") + if(!isnull(size)) + .["size"] = size + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(offset)) + .["offset"] = offset + if(!isnull(flags)) + .["flags"] = flags + +/proc/apply_wibbly_filters(atom/in_atom, length) + for(var/i in 1 to 7) + //This is a very baffling and strange way of doing this but I am just preserving old functionality + var/X + var/Y + var/rsq + do + X = 60*rand() - 30 + Y = 60*rand() - 30 + rsq = X*X + Y*Y + while(rsq<100 || rsq>900) // Yeah let's just loop infinitely due to bad luck what's the worst that could happen? + var/random_roll = rand() + in_atom.add_filter("wibbly-[i]", 5, wave_filter(x = X, y = Y, size = rand() * 2.5 + 0.5, offset = random_roll)) + var/filter = in_atom.get_filter("wibbly-[i]") + animate(filter, offset = random_roll, time = 0, loop = -1, flags = ANIMATION_PARALLEL) + animate(offset = random_roll - 1, time = rand() * 20 + 10) + +/proc/remove_wibbly_filters(atom/in_atom) + var/filter + for(var/i in 1 to 7) + filter = in_atom.get_filter("wibbly-[i]") + animate(filter) + in_atom.remove_filter("wibbly-[i]") diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 1f5aeabc692..26a16343bee 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1621,16 +1621,3 @@ config_setting should be one of the following: var/list/servers = CONFIG_GET(keyed_list/insecure_cross_server) for(var/I in servers) world.Export("[servers[I]]?[list2params(message)]") - -/proc/drop_shadow_filter(x, y, size, offset, color) - . = list("type" = "drop_shadow") - if(!isnull(x)) - .["x"] = x - if(!isnull(y)) - .["y"] = y - if(!isnull(size)) - .["size"] = size - if(!isnull(offset)) - .["offset"] = offset - if(!isnull(color)) - .["color"] = color diff --git a/code/_onclick/hud/plane_master.dm b/code/_onclick/hud/plane_master.dm index 7c073e20852..6e2f59a3005 100644 --- a/code/_onclick/hud/plane_master.dm +++ b/code/_onclick/hud/plane_master.dm @@ -30,14 +30,11 @@ filters += filter(type = "drop_shadow", color = "#04080FAA", size = -15) filters += filter(type = "drop_shadow", color = "#04080FAA", size = -20) -/atom/movable/screen/plane_master/proc/outline(_size, _color) - filters += filter(type = "outline", size = _size, color = _color) - -/atom/movable/screen/plane_master/proc/shadow(_size, _border, _offset = 0, _x = 0, _y = 0, _color = "#04080FAA") - filters += filter(type = "drop_shadow", x = _x, y = _y, color = _color, size = _size, offset = _offset) - -/atom/movable/screen/plane_master/proc/clear_filters() - filters = list() +/atom/movable/screen/plane_master/openspace/Initialize() + . = ..() + add_filter("first_stage_openspace", 1, drop_shadow_filter(color = "#04080FAA", size = -10)) + add_filter("second_stage_openspace", 2, drop_shadow_filter(color = "#04080FAA", size = -15)) + add_filter("third_stage_openspace", 2, drop_shadow_filter(color = "#04080FAA", size = -20)) ///Contains just the floor /atom/movable/screen/plane_master/floor @@ -47,9 +44,9 @@ blend_mode = BLEND_OVERLAY /atom/movable/screen/plane_master/floor/backdrop(mob/mymob) - filters = list() + clear_filters() if(istype(mymob) && mymob.eye_blurry) - filters += GAUSSIAN_BLUR(CLAMP(mymob.eye_blurry*0.1,0.6,3)) + add_filter("eye_blur", 1, gauss_blur_filter(clamp(mymob.eye_blurry * 0.1, 0.6, 3))) ///Contains most things in the game world /atom/movable/screen/plane_master/game_world @@ -59,11 +56,12 @@ blend_mode = BLEND_OVERLAY /atom/movable/screen/plane_master/game_world/backdrop(mob/mymob) - filters = list() + clear_filters() if(istype(mymob) && mymob.client && mymob.client.prefs && mymob.client.prefs.ambientocclusion) - filters += AMBIENT_OCCLUSION + add_filter("AO", 1, drop_shadow_filter(x = 0, y = -2, size = 4, color = "#04080FAA")) if(istype(mymob) && mymob.eye_blurry) - filters += GAUSSIAN_BLUR(CLAMP(mymob.eye_blurry*0.1,0.6,3)) + add_filter("eye_blur", 1, gauss_blur_filter(clamp(mymob.eye_blurry * 0.1, 0.6, 3))) + ///Contains all lighting objects /atom/movable/screen/plane_master/lighting @@ -74,8 +72,8 @@ /atom/movable/screen/plane_master/lighting/Initialize() . = ..() - filters += filter(type="alpha", render_source=EMISSIVE_RENDER_TARGET, flags=MASK_INVERSE) - filters += filter(type="alpha", render_source=EMISSIVE_UNBLOCKABLE_RENDER_TARGET, flags=MASK_INVERSE) + add_filter("emissives", 1, alpha_mask_filter(render_source = EMISSIVE_RENDER_TARGET, flags = MASK_INVERSE)) + add_filter("unblockable_emissives", 2, alpha_mask_filter(render_source = EMISSIVE_UNBLOCKABLE_RENDER_TARGET, flags = MASK_INVERSE)) /** * Things placed on this mask the lighting plane. Doesn't render directly. @@ -91,7 +89,7 @@ /atom/movable/screen/plane_master/emissive/Initialize() . = ..() - filters += filter(type="alpha", render_source=EMISSIVE_BLOCKER_RENDER_TARGET, flags=MASK_INVERSE) + add_filter("emissive_block", 1, alpha_mask_filter(render_source = EMISSIVE_BLOCKER_RENDER_TARGET, flags = MASK_INVERSE)) /** * Things placed on this always mask the lighting plane. Doesn't render directly. @@ -147,4 +145,4 @@ /atom/movable/screen/plane_master/runechat/backdrop(mob/mymob) filters = list() if(istype(mymob) && mymob.client?.prefs?.ambientocclusion) - add_filter("AO", 1, drop_shadow_filter(x = 0, y = -2, size = 1, color = "#04080f42")) + add_filter("AO", 1, drop_shadow_filter(x = 0, y = -2, size = 4, color = "#04080FAA")) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 770230b5064..039215f0b27 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -936,6 +936,11 @@ VV_DROPDOWN_OPTION(VV_HK_ADD_REAGENT, "Add Reagent") VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EMP, "EMP Pulse") VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EXPLOSION, "Explosion") +<<<<<<< HEAD +======= + VV_DROPDOWN_OPTION(VV_HK_EDIT_FILTERS, "Edit Filters") + VV_DROPDOWN_OPTION(VV_HK_ADD_AI, "Add AI controller") +>>>>>>> 203156d3e2 (ports tg's filter debug menu & minor filter refactor (#4434)) /atom/vv_do_topic(list/href_list) . = ..() @@ -999,6 +1004,10 @@ if(newname) vv_auto_rename(newname) + if(href_list[VV_HK_EDIT_FILTERS] && check_rights(R_VAREDIT)) + var/client/C = usr.client + C?.open_filter_editor(src) + /atom/vv_get_header() . = ..() var/refid = REF(src) @@ -1210,14 +1219,14 @@ var/reverse_message = "has been [what_done] by [ssource][postfix]" target.log_message(reverse_message, LOG_ATTACK, color="orange", log_globally=FALSE) -/atom/movable/proc/add_filter(name,priority,list/params) +/atom/proc/add_filter(name,priority,list/params) LAZYINITLIST(filter_data) var/list/p = params.Copy() p["priority"] = priority filter_data[name] = p update_filters() -/atom/movable/proc/update_filters() +/atom/proc/update_filters() filters = null filter_data = sortTim(filter_data, /proc/cmp_filter_data_priority, TRUE) for(var/f in filter_data) @@ -1225,15 +1234,48 @@ var/list/arguments = data.Copy() arguments -= "priority" filters += filter(arglist(arguments)) + UNSETEMPTY(filter_data) + +/atom/proc/transition_filter(name, time, list/new_params, easing, loop) + var/filter = get_filter(name) + if(!filter) + return + + var/list/old_filter_data = filter_data[name] + + var/list/params = old_filter_data.Copy() + for(var/thing in new_params) + params[thing] = new_params[thing] -/atom/movable/proc/get_filter(name) + animate(filter, new_params, time = time, easing = easing, loop = loop) + for(var/param in params) + filter_data[name][param] = params[param] + +/atom/proc/change_filter_priority(name, new_priority) + if(!filter_data || !filter_data[name]) + return + + filter_data[name]["priority"] = new_priority + update_filters() + +/atom/proc/get_filter(name) if(filter_data && filter_data[name]) return filters[filter_data.Find(name)] -/atom/movable/proc/remove_filter(name) - if(filter_data && filter_data[name]) - filter_data -= name - update_filters() +/atom/proc/remove_filter(name_or_names) + if(!filter_data) + return + + var/list/names = islist(name_or_names) ? name_or_names : list(name_or_names) + + for(var/name in names) + if(filter_data[name]) + filter_data -= name + update_filters() + +/atom/proc/clear_filters() + filter_data = null + filters = null /atom/proc/intercept_zImpact(atom/movable/AM, levels = 1) . |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels) diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index 0f43388cf22..694b60c13d5 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -31,6 +31,8 @@ GLOBAL_PROTECT(href_token) //Admin help manager var/datum/admin_help_ui/admin_interface + var/datum/filter_editor/filteriffic + /datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE, protected) if(IsAdminAdvancedProcCall()) var/msg = " has tried to elevate permissions!" diff --git a/code/modules/admin/view_variables/filterrific.dm b/code/modules/admin/view_variables/filterrific.dm new file mode 100644 index 00000000000..e651028cbe6 --- /dev/null +++ b/code/modules/admin/view_variables/filterrific.dm @@ -0,0 +1,99 @@ +/datum/filter_editor + var/atom/target + +/datum/filter_editor/New(atom/target) + src.target = target + +/datum/filter_editor/ui_state(mob/user) + return GLOB.admin_state + +/datum/filter_editor/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Filteriffic") + ui.open() + +/datum/filter_editor/ui_static_data(mob/user) + var/list/data = list() + data["filter_info"] = GLOB.master_filter_info + return data + +/datum/filter_editor/ui_data() + var/list/data = list() + data["target_name"] = target.name + data["target_filter_data"] = target.filter_data + return data + +/datum/filter_editor/ui_act(action, list/params) + . = ..() + if(.) + return + + switch(action) + if("add_filter") + var/target_name = params["name"] + while(target.filter_data && target.filter_data[target_name]) + target_name = "[target_name]-dupe" + target.add_filter(target_name, params["priority"], list("type" = params["type"])) + . = TRUE + if("remove_filter") + target.remove_filter(params["name"]) + . = TRUE + if("rename_filter") + var/list/filter_data = target.filter_data[params["name"]] + target.remove_filter(params["name"]) + target.add_filter(params["new_name"], filter_data["priority"], filter_data) + . = TRUE + if("edit_filter") + target.remove_filter(params["name"]) + target.add_filter(params["name"], params["priority"], params["new_filter"]) + . = TRUE + if("change_priority") + var/new_priority = params["new_priority"] + target.change_filter_priority(params["name"], new_priority) + . = TRUE + if("transition_filter_value") + target.transition_filter(params["name"], 4, params["new_data"]) + . = TRUE + if("modify_filter_value") + var/list/old_filter_data = target.filter_data[params["name"]] + var/list/new_filter_data = old_filter_data.Copy() + for(var/entry in params["new_data"]) + new_filter_data[entry] = params["new_data"][entry] + for(var/entry in new_filter_data) + if(entry == GLOB.master_filter_info[old_filter_data["type"]]["defaults"][entry]) + new_filter_data.Remove(entry) + target.remove_filter(params["name"]) + target.add_filter(params["name"], old_filter_data["priority"], new_filter_data) + . = TRUE + if("modify_color_value") + var/new_color = input(usr, "Pick new filter color", "Filteriffic Colors!") as color|null + if(new_color) + target.transition_filter(params["name"], 4, list("color" = new_color)) + . = TRUE + if("modify_icon_value") + var/icon/new_icon = input("Pick icon:", "Icon") as null|icon + if(new_icon) + target.filter_data[params["name"]]["icon"] = new_icon + target.update_filters() + . = TRUE + if("mass_apply") + if(!check_rights_for(usr.client, R_FUN)) + to_chat(usr, "You activate \the [src].") - playsound(src, 'sound/effects/seedling_chargeup.ogg', 100, 1, -6) - var/start = user.filters.len - var/X,Y,rsq,i,f - for(i=1, i<=7, ++i) - do - X = 60*rand() - 30 - Y = 60*rand() - 30 - rsq = X*X + Y*Y - while(rsq<100 || rsq>900) - user.filters += filter(type="wave", x=X, y=Y, size=rand()*2.5+0.5, offset=rand()) - for(i=1, i<=7, ++i) - f = user.filters[start+i] - animate(f, offset=f:offset, time=0, loop=3, flags=ANIMATION_PARALLEL) - animate(offset=f:offset-1, time=rand()*20+10) + playsound(src, 'sound/effects/seedling_chargeup.ogg', 100, TRUE, -6) + apply_wibbly_filters(user) if (do_after(user, 50, target=user) && user.cell.use(activationCost)) playsound(src, 'sound/effects/bamf.ogg', 100, 1, -6) to_chat(user, "You are now disguised as the Nanotrasen engineering borg \"[friendlyName]\".") @@ -84,10 +72,7 @@ else to_chat(user, "The chameleon field fizzles.") do_sparks(3, FALSE, user) - for(i=1, i<=min(7, user.filters.len), ++i) // removing filters that are animating does nothing, we gotta stop the animations first - f = user.filters[start+i] - animate(f) - user.filters = null + remove_wibbly_filters(user) animation_playing = FALSE /obj/item/borg_chameleon/process() diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm index 4c0e03eb4fc..dced2450e60 100644 --- a/code/modules/cargo/supplypod.dm +++ b/code/modules/cargo/supplypod.dm @@ -631,7 +631,7 @@ smoke_effects[i] = smoke_part smoke_part.pixel_x = sin(rotation)*32 * i smoke_part.pixel_y = abs(cos(rotation))*32 * i - smoke_part.filters += filter(type = "blur", size = 4) + smoke_part.add_filter("smoke_blur", 1, gauss_blur_filter(size = 4)) var/time = (pod.delays[POD_FALLING] / length(smoke_effects))*(length(smoke_effects)-i) addtimer(CALLBACK(smoke_part, /obj/effect/supplypod_smoke/.proc/drawSelf, i), time, TIMER_CLIENT_TIME) //Go onto the last step after a very short falling animation QDEL_IN(smoke_part, pod.delays[POD_FALLING] + 35) @@ -641,7 +641,7 @@ return for (var/obj/effect/supplypod_smoke/smoke_part in smoke_effects) animate(smoke_part, alpha = 0, time = 20, flags = ANIMATION_PARALLEL) - animate(smoke_part.filters[1], size = 6, time = 15, easing = CUBIC_EASING|EASE_OUT, flags = ANIMATION_PARALLEL) + animate(smoke_part.get_filter("smoke_blur"), size = 6, time = 15, easing = CUBIC_EASING|EASE_OUT, flags = ANIMATION_PARALLEL) /obj/effect/pod_landingzone/proc/endLaunch() pod.tryMakeRubble(drop_location()) diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index e7179cc6153..6f838945350 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -1104,3 +1104,18 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( qdel(src) return +<<<<<<< HEAD +======= +/client/proc/open_filter_editor(atom/in_atom) + if(holder) + holder.filteriffic = new /datum/filter_editor(in_atom) + holder.filteriffic.ui_interact(mob) + +/client/proc/update_ambience_pref() + if(prefs.toggles & SOUND_AMBIENCE) + if(SSambience.ambience_listening_clients[src] > world.time) + return // If already properly set we don't want to reset the timer. + SSambience.ambience_listening_clients[src] = world.time + 10 SECONDS //Just wait 10 seconds before the next one aight mate? cheers. + else + SSambience.ambience_listening_clients -= src +>>>>>>> 203156d3e2 (ports tg's filter debug menu & minor filter refactor (#4434)) diff --git a/tgui/docs/component-reference.md b/tgui/docs/component-reference.md index 4e56377ddcf..db72d37b521 100644 --- a/tgui/docs/component-reference.md +++ b/tgui/docs/component-reference.md @@ -359,12 +359,16 @@ and displays selected entry. **Props:** - See inherited props: [Box](#box) +- See inherited props: [Icon](#icon) - `options: string[]` - An array of strings which will be displayed in the dropdown when open - `selected: string` - Currently selected entry - `width: number` - Width of dropdown button and resulting menu -- `over: boolean` - dropdown renders over instead of below -- `color: string` - color of dropdown button +- `over: boolean` - Dropdown renders over instead of below +- `color: string` - Color of dropdown button +- `nochevron: boolean` - Whether or not the arrow on the right hand side of the dropdown button is visible +- `noscroll: boolean` - Whether or not the dropdown menu should have a scroll bar +- `displayText: string` - Text to always display in place of the selected text - `onClick: (e) => void` - Called when dropdown button is clicked - `onSelected: (value) => void` - Called when a value is picked from the list, `value` is the value that was picked diff --git a/tgui/packages/common/math.js b/tgui/packages/common/math.js index c3013b5a32a..df7e395ea79 100644 --- a/tgui/packages/common/math.js +++ b/tgui/packages/common/math.js @@ -88,3 +88,13 @@ export const keyOfMatchingRange = (value, ranges) => { } } }; + +/** + * Get number of digits following the decimal point in a number + */ +export const numberOfDecimalDigits = value => { + if (Math.floor(value) !== value) { + return value.toString().split('.')[1].length || 0; + } + return 0; +}; diff --git a/tgui/packages/tgui/components/Dropdown.js b/tgui/packages/tgui/components/Dropdown.js index adbb13de5b2..7486807e3f3 100644 --- a/tgui/packages/tgui/components/Dropdown.js +++ b/tgui/packages/tgui/components/Dropdown.js @@ -64,6 +64,9 @@ export class Dropdown extends Component { render() { const { props } = this; const { + icon, + iconRotation, + iconSpin, color = 'default', over, noscroll, @@ -72,6 +75,7 @@ export class Dropdown extends Component { onClick, selected, disabled, + displayText, ...boxProps } = props; const { @@ -114,8 +118,15 @@ export class Dropdown extends Component { } this.setOpen(!this.state.open); }}> + {icon && ( + + )} - {this.state.selected} + {displayText ? displayText : this.state.selected} {!!nochevron || ( diff --git a/tgui/packages/tgui/interfaces/Filteriffic.js b/tgui/packages/tgui/interfaces/Filteriffic.js new file mode 100644 index 00000000000..39695ea54c8 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Filteriffic.js @@ -0,0 +1,307 @@ +import { useBackend, useLocalState } from "../backend"; +import { Fragment } from 'inferno'; +import { Box, Button, Collapsible, ColorBox, Dropdown, Input, LabeledList, NoticeBox, NumberInput, Section } from '../components'; +import { Window } from '../layouts'; +import { map } from 'common/collections'; +import { toFixed } from 'common/math'; +import { numberOfDecimalDigits } from "../../common/math"; + +const FilterIntegerEntry = (props, context) => { + const { value, name, filterName } = props; + const { act } = useBackend(context); + return ( + act('modify_filter_value', { + name: filterName, + new_data: { + [name]: value, + }, + })} /> + ); +}; + +const FilterFloatEntry = (props, context) => { + const { value, name, filterName } = props; + const { act } = useBackend(context); + const [step, setStep] = useLocalState(context, `${filterName}-${name}`, 0.01); + return ( + + toFixed(value, numberOfDecimalDigits(step))} + width="80px" + onDrag={(e, value) => act('transition_filter_value', { + name: filterName, + new_data: { + [name]: value, + }, + })} /> + + Step: + + toFixed(value, 4)} + width="70px" + onChange={(e, value) => setStep(value)} /> + + ); +}; + +const FilterTextEntry = (props, context) => { + const { value, name, filterName } = props; + const { act } = useBackend(context); + + return ( + act('modify_filter_value', { + name: filterName, + new_data: { + [name]: value, + }, + })} /> + ); +}; + +const FilterColorEntry = (props, context) => { + const { value, filterName, name } = props; + const { act } = useBackend(context); + return ( + +