mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-24 13:38:41 +01:00
Implements color matrix and transform modification support for Filterrific (#93867)
## About The Pull Request Allows users to edit color matrix filters and layered filters' transforms through Filterrific, which were the two last unsupported filter fields. The menus are pretty minimal, but you shouldn't be touching them if you don't know what you're doing anyways. https://github.com/user-attachments/assets/e87447f3-e5d6-4f6c-a8e4-bf50a1d5fa79 ## Why It's Good For The Game Makes our tooling actually work ## Changelog 🆑 admin: Implemented color matrix and transform modification support for Filterrific /🆑
This commit is contained in:
@@ -32,7 +32,7 @@ GLOBAL_PROTECT(href_token)
|
||||
|
||||
var/deadmined
|
||||
|
||||
var/datum/filter_editor/filteriffic
|
||||
var/datum/filter_editor/filterrific
|
||||
var/datum/particle_editor/particle_test
|
||||
var/datum/colorblind_tester/color_test
|
||||
var/datum/plane_master_debug/plane_debug
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/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 = new(user, src, "Filterrific")
|
||||
ui.open()
|
||||
|
||||
/datum/filter_editor/ui_static_data(mob/user)
|
||||
@@ -21,7 +21,14 @@
|
||||
/datum/filter_editor/ui_data()
|
||||
var/list/data = list()
|
||||
data["target_name"] = target.name
|
||||
data["target_filter_data"] = target.filter_data
|
||||
var/list/target_filter_data = list()
|
||||
for (var/list/filter_info as anything in target.filter_data)
|
||||
filter_info = deep_copy_list(filter_info)
|
||||
if (filter_info["transform"])
|
||||
var/matrix/filter_transform = filter_info["transform"]
|
||||
filter_info["transform"] = list("a" = filter_transform.a, "b" = filter_transform.b, "c" = filter_transform.c, "d" = filter_transform.d, "e" = filter_transform.e, "f" = filter_transform.f)
|
||||
target_filter_data += list(filter_info)
|
||||
data["target_filter_data"] = target_filter_data
|
||||
return data
|
||||
|
||||
/datum/filter_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
@@ -59,7 +66,7 @@
|
||||
target.modify_filter(params["name"], params["new_data"])
|
||||
. = TRUE
|
||||
if("modify_color_value")
|
||||
var/new_color = input(usr, "Pick new filter color", "Filteriffic Colors!") as color|null
|
||||
var/new_color = input(usr, "Pick new filter color", "Filterrific Colors!") as color|null
|
||||
if(new_color)
|
||||
target.transition_filter(params["name"], list("color" = new_color), 4)
|
||||
. = TRUE
|
||||
@@ -68,6 +75,16 @@
|
||||
if(new_icon)
|
||||
target.modify_filter(params["name"], list("icon" = new_icon))
|
||||
. = TRUE
|
||||
if("modify_transform_value")
|
||||
var/list/filter_info = target.get_filter_data(params["name"])
|
||||
if (!filter_info)
|
||||
return
|
||||
var/matrix/new_transform = matrix(filter_info[params["field_name"]])
|
||||
if (!(params["transform_key"] in list("a", "b", "c", "d", "e", "f")))
|
||||
return
|
||||
new_transform.vars[params["transform_key"]] = text2num(params["transform_value"])
|
||||
target.modify_filter(params["name"], list(params["field_name"] = new_transform))
|
||||
. = TRUE
|
||||
if("mass_apply")
|
||||
if(!check_rights_for(usr.client, R_FUN))
|
||||
to_chat(usr, span_userdanger("Stay in your lane, jannie."))
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* More than just a completely true statement, this datum is created as a tgui interface
|
||||
* allowing you to modify each vector until you know what you're doing.
|
||||
* Much like filteriffic, 'nobody wants to learn matrix math' is meant for developers like you and I
|
||||
* Much like filterrific, 'nobody wants to learn matrix math' is meant for developers like you and I
|
||||
* to implement interesting matrix transformations without the hassle if needing to know... algebra? Damn, i'm stupid.
|
||||
*/
|
||||
/datum/nobody_wants_to_learn_matrix_math
|
||||
|
||||
@@ -1126,8 +1126,8 @@ GLOBAL_LIST_INIT(unrecommended_builds, list(
|
||||
|
||||
/client/proc/open_filter_editor(atom/in_atom)
|
||||
if(holder)
|
||||
holder.filteriffic = new /datum/filter_editor(in_atom)
|
||||
holder.filteriffic.ui_interact(mob)
|
||||
holder.filterrific = new /datum/filter_editor(in_atom)
|
||||
holder.filterrific.ui_interact(mob)
|
||||
|
||||
///opens the particle editor UI for the in_atom object for this client
|
||||
/client/proc/open_particle_editor(atom/movable/in_atom)
|
||||
|
||||
+160
-4
@@ -11,6 +11,7 @@ import {
|
||||
NoticeBox,
|
||||
NumberInput,
|
||||
Section,
|
||||
Stack,
|
||||
} from 'tgui-core/components';
|
||||
import { numberOfDecimalDigits, toFixed } from 'tgui-core/math';
|
||||
|
||||
@@ -193,6 +194,159 @@ const FilterOptionsEntry = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const FilterTransformEntry = (props) => {
|
||||
const { value, name, filterName } = props;
|
||||
const { act } = useBackend();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack>
|
||||
{['a', 'b', 'c'].map((letter_key) => (
|
||||
<Stack.Item key={letter_key}>
|
||||
<Box inline ml={2} mr={1}>
|
||||
{letter_key}:
|
||||
</Box>
|
||||
<NumberInput
|
||||
value={value ? value[letter_key] || 0 : 0}
|
||||
minValue={letter_key === 'c' ? -480 : -4}
|
||||
maxValue={letter_key === 'c' ? 480 : 4}
|
||||
step={letter_key === 'c' ? 1 : 0.01}
|
||||
stepPixelSize={5}
|
||||
width="39px"
|
||||
onChange={(value) =>
|
||||
act('modify_transform_value', {
|
||||
name: filterName,
|
||||
field_name: name,
|
||||
transform_key: letter_key,
|
||||
transform_value: value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Stack.Item>
|
||||
))}
|
||||
</Stack>
|
||||
<Stack>
|
||||
{['d', 'e', 'f'].map((letter_key) => (
|
||||
<Stack.Item key={letter_key}>
|
||||
<Box inline ml={2} mr={1}>
|
||||
{letter_key}:
|
||||
</Box>
|
||||
<NumberInput
|
||||
value={value ? value[letter_key] || 0 : 0}
|
||||
minValue={letter_key === 'f' ? -480 : -4}
|
||||
maxValue={letter_key === 'f' ? 480 : 4}
|
||||
step={letter_key === 'f' ? 1 : 0.01}
|
||||
stepPixelSize={5}
|
||||
width="39px"
|
||||
onChange={(value) =>
|
||||
act('modify_transform_value', {
|
||||
name: filterName,
|
||||
field_name: name,
|
||||
transform_key: letter_key,
|
||||
transform_value: value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Stack.Item>
|
||||
))}
|
||||
</Stack>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const FilterMatrixEntry = (props) => {
|
||||
const { name, value, filterName, filterType } = props;
|
||||
const { act } = useBackend();
|
||||
const matrix_sizes = [9, 12, 16, 20];
|
||||
const resize_matrix = (matrix, size) => {
|
||||
let identity = [1, 0, 0, 0, 1, 0, 0, 0, 1];
|
||||
switch (size) {
|
||||
case 12:
|
||||
identity = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0];
|
||||
break;
|
||||
case 16:
|
||||
identity = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
||||
break;
|
||||
case 20:
|
||||
identity = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0];
|
||||
break;
|
||||
}
|
||||
if (matrix === null || matrix === undefined) return identity;
|
||||
|
||||
for (let i = 0; i < Math.min(size, matrix.length); i++) {
|
||||
if (matrix.length === 9)
|
||||
identity[i + Math.floor(i / 3)] = matrix[i]; // Account for skipped constants
|
||||
else identity[i] = matrix[i];
|
||||
}
|
||||
return identity;
|
||||
};
|
||||
|
||||
let matrix = value;
|
||||
|
||||
if (value === null || value === undefined) {
|
||||
matrix = resize_matrix(value, 9);
|
||||
}
|
||||
|
||||
const processed_matrix = [];
|
||||
const row_width = matrix.length > 9 ? 4 : 3;
|
||||
for (let i = 0; i < (matrix.length > 9 ? matrix.length / 4 : 3); i++) {
|
||||
const new_row = [];
|
||||
for (let j = 0; j < row_width; j++) {
|
||||
new_row.push(matrix[i * row_width + j]);
|
||||
}
|
||||
processed_matrix.push(new_row);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Dropdown
|
||||
displayText="Matrix Size"
|
||||
selected={matrix.length}
|
||||
options={matrix_sizes.map((size) => `${size} elements`)}
|
||||
onSelected={(option) =>
|
||||
matrix.length === parseInt(option.split(' '))
|
||||
? null
|
||||
: act('modify_filter_value', {
|
||||
name: filterName,
|
||||
new_data: {
|
||||
[name]: resize_matrix(matrix, parseInt(option.split(' '))),
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Stack vertical>
|
||||
{processed_matrix.map((matrix_row, row_index) => (
|
||||
<Stack.Item key={row_index}>
|
||||
<Stack>
|
||||
{matrix_row.map((matrix_elem, elem_index) => (
|
||||
<Stack.Item key={elem_index}>
|
||||
<NumberInput
|
||||
value={matrix[row_index * row_width + elem_index]}
|
||||
minValue={-4}
|
||||
maxValue={4}
|
||||
step={0.01}
|
||||
stepPixelSize={5}
|
||||
width="39px"
|
||||
onChange={(value) => {
|
||||
matrix[row_index * row_width + elem_index] = value;
|
||||
act('transition_filter_value', {
|
||||
name: filterName,
|
||||
new_data: {
|
||||
[name]: matrix,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Stack.Item>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
))}
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const FilterDataEntry = (props) => {
|
||||
const { name, value, hasValue, filterName, filterType } = props;
|
||||
|
||||
@@ -204,6 +358,8 @@ const FilterDataEntry = (props) => {
|
||||
icon: <FilterIconEntry {...props} />,
|
||||
flags: <FilterFlagsEntry {...props} />,
|
||||
options: <FilterOptionsEntry {...props} />,
|
||||
transform: <FilterTransformEntry {...props} />,
|
||||
matrix: <FilterMatrixEntry {...props} />,
|
||||
plug: 'Not Implemented',
|
||||
};
|
||||
|
||||
@@ -214,7 +370,7 @@ const FilterDataEntry = (props) => {
|
||||
render_source: 'string',
|
||||
flags: 'flags',
|
||||
size: 'float',
|
||||
color: { default: 'color', color: 'plug' },
|
||||
color: { default: 'color', color: 'matrix' },
|
||||
offset: 'float',
|
||||
radius: 'int',
|
||||
falloff: 'float',
|
||||
@@ -225,7 +381,7 @@ const FilterDataEntry = (props) => {
|
||||
repeat: 'int',
|
||||
space: 'options',
|
||||
blend_mode: 'options',
|
||||
transform: 'plug',
|
||||
transform: 'transform',
|
||||
};
|
||||
|
||||
let filterInputType = filterEntryMap[name];
|
||||
@@ -315,7 +471,7 @@ const FilterEntry = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const Filteriffic = (props) => {
|
||||
export const Filterrific = (props) => {
|
||||
const { act, data } = useBackend();
|
||||
const name = data.target_name || 'Unknown Object';
|
||||
const filters = data.target_filter_data || {};
|
||||
@@ -325,7 +481,7 @@ export const Filteriffic = (props) => {
|
||||
const [hiddenSecret, setHiddenSecret] = useState(false);
|
||||
|
||||
return (
|
||||
<Window title="Filteriffic" width={500} height={500}>
|
||||
<Window title="Filterrific" width={500} height={500}>
|
||||
<Window.Content scrollable>
|
||||
<NoticeBox danger>
|
||||
DO NOT MESS WITH EXISTING FILTERS IF YOU DO NOT KNOW THE CONSEQUENCES.
|
||||
Reference in New Issue
Block a user