Skip to content

Commit

Permalink
tgui body color/type/size picker (#7475)
Browse files Browse the repository at this point in the history
vido


https://github.com/user-attachments/assets/be1b699a-4bda-4343-8d13-90f979ff19ac

🆑
add: a new tgui with better previews for the different skin colors /
body types / body sizes
/🆑

---------

Co-authored-by: Drathek <[email protected]>
  • Loading branch information
harryob and Drulikar authored Nov 3, 2024
1 parent a28cf1d commit c7e20e4
Show file tree
Hide file tree
Showing 8 changed files with 303 additions and 21 deletions.
74 changes: 74 additions & 0 deletions code/modules/client/color_picker.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/datum/body_picker/ui_static_data(mob/user)
. = ..()

.["icon"] = /datum/species::icobase

.["body_types"] = list()
for(var/key in GLOB.body_type_list)
var/datum/body_type/type = GLOB.body_type_list[key]
.["body_types"] += list(
list("name" = type.name, "icon" = type.icon_name)
)

.["skin_colors"] = list()
for(var/key in GLOB.skin_color_list)
var/datum/skin_color/color = GLOB.skin_color_list[key]
.["skin_colors"] += list(
list("name" = color.name, "icon" = color.icon_name, "color" = color.color)
)

.["body_sizes"] = list()
for(var/key in GLOB.body_size_list)
var/datum/body_size/size = GLOB.body_size_list[key]
.["body_sizes"] += list(
list("name" = size.name, "icon" = size.icon_name)
)

/datum/body_picker/ui_data(mob/user)
. = ..()

.["body_type"] = GLOB.body_type_list[user.client.prefs.body_type].icon_name
.["skin_color"] = GLOB.skin_color_list[user.client.prefs.skin_color].icon_name
.["body_size"] = GLOB.body_size_list[user.client.prefs.body_size].icon_name

/datum/body_picker/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()

var/datum/preferences/prefs = ui.user.client.prefs

switch(action)
if("type")
if(!GLOB.body_type_list[params["name"]])
return

prefs.body_type = params["name"]

if("size")
if(!GLOB.body_size_list[params["name"]])
return

prefs.body_size = params["name"]

if("color")
if(!GLOB.skin_color_list[params["name"]])
return

prefs.skin_color = params["name"]

prefs.ShowChoices(ui.user)
return TRUE

/datum/body_picker/tgui_interact(mob/user, datum/tgui/ui)
. = ..()

ui = SStgui.try_update_ui(user, src, ui)

if(!ui)
ui = new(user, src, "BodyPicker", "Body Picker")
ui.open()
ui.set_autoupdate(FALSE)

winset(user, ui.window.id, "focus=true")

/datum/body_picker/ui_state(mob/user)
return GLOB.always_state
33 changes: 12 additions & 21 deletions code/modules/client/preferences.dm
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ GLOBAL_LIST_INIT(bgstate_options, list(
var/atom/movable/screen/rotate/alt/rotate_left
var/atom/movable/screen/rotate/rotate_right

var/static/datum/body_picker/picker = new

//doohickeys for savefiles
var/path
var/default_slot = 1 //Holder so it doesn't default to slot 1, rather the last one used
Expand Down Expand Up @@ -340,10 +342,13 @@ GLOBAL_LIST_INIT(bgstate_options, list(
dat += "<h2><b><u>Physical Information:</u></b>"
dat += "<a href='?_src_=prefs;preference=all;task=random'>&reg;</A></h2>"
dat += "<b>Age:</b> <a href='?_src_=prefs;preference=age;task=input'><b>[age]</b></a><br>"
dat += "<b>Gender:</b> <a href='?_src_=prefs;preference=gender'><b>[gender == MALE ? "Male" : "Female"]</b></a><br>"
dat += "<b>Skin Color:</b> <a href='?_src_=prefs;preference=skin_color;task=input'><b>[skin_color]</b></a><br>"
dat += "<b>Body Size:</b> <a href='?_src_=prefs;preference=body_size;task=input'><b>[body_size]</b></a><br>"
dat += "<b>Body Muscularity:</b> <a href='?_src_=prefs;preference=body_type;task=input'><b>[body_type]</b></a><br>"
dat += "<b>Gender:</b> <a href='?_src_=prefs;preference=gender'><b>[gender == MALE ? "Male" : "Female"]</b></a><br><br>"

dat += "<b>Skin Color:</b> [skin_color]<br>"
dat += "<b>Body Size:</b> [body_size]<br>"
dat += "<b>Body Muscularity:</b> [body_type]<br>"
dat += "<b>Edit Body:</b> <a href='?_src_=prefs;preference=body;task=input'><b>Picker</b></a><br><br>"

dat += "<b>Traits:</b> <a href='byond://?src=\ref[user];preference=traits;task=open'><b>Character Traits</b></a>"
dat += "<br>"

Expand Down Expand Up @@ -1569,23 +1574,9 @@ GLOBAL_LIST_INIT(bgstate_options, list(
if(new_h_gradient_style)
grad_style = new_h_gradient_style

if ("skin_color")
var/new_skin_color = tgui_input_list(user, "Choose your character's skin color:", "Character Preferences", GLOB.skin_color_list)

if (new_skin_color)
skin_color = new_skin_color

if ("body_size")
var/new_body_size = tgui_input_list(user, "Choose your character's body size:", "Character Preferences", GLOB.body_size_list)

if (new_body_size)
body_size = new_body_size

if ("body_type")
var/new_body_type = tgui_input_list(user, "Choose your character's body type:", "Character Preferences", GLOB.body_type_list)

if (new_body_type)
body_type = new_body_type
if ("body")
picker.tgui_interact(user)
return

if("facial")
var/new_facial = input(user, "Choose your character's facial-hair color:", "Character Preference", rgb(r_facial, g_facial, b_facial)) as color|null
Expand Down
8 changes: 8 additions & 0 deletions code/modules/mob/new_player/skin_color.dm
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
var/name
var/icon_name

var/color

/datum/skin_color/New()
. = ..()

var/icon/icon_to_use = icon(/datum/species::icobase, "[icon_name]_torso_[/datum/body_size/thin::icon_name]_[/datum/body_type/twig::icon_name]")
color = icon_to_use.GetPixel(icon_to_use.Width() / 2, icon_to_use.Height() / 2)

/datum/skin_color/cmplayer
name = "Extra Pale"
icon_name = "cmp1"
Expand Down
1 change: 1 addition & 0 deletions colonialmarines.dme
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,7 @@
#include "code\modules\clans\ship.dm"
#include "code\modules\client\client_defines.dm"
#include "code\modules\client\client_procs.dm"
#include "code\modules\client\color_picker.dm"
#include "code\modules\client\country_flags.dm"
#include "code\modules\client\player_details.dm"
#include "code\modules\client\preferences.dm"
Expand Down
183 changes: 183 additions & 0 deletions tgui/packages/tgui/interfaces/BodyPicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import { useState } from 'react';

import { useBackend } from '../backend';
import {
Box,
Button,
ColorBox,
DmIcon,
Modal,
Stack,
Tooltip,
} from '../components';
import { Window } from '../layouts';

type PickerData = {
icon: string;
body_types: { name: string; icon: string }[];
skin_colors: { name: string; icon: string; color: string }[];
body_sizes: { name: string; icon: string }[];

body_type: string;
skin_color: string;
body_size: string;
};

export const BodyPicker = () => {
const { data } = useBackend<PickerData>();

const { icon, body_size, body_type, skin_color, body_types, body_sizes } =
data;

const [picker, setPicker] = useState<'type' | 'size' | undefined>();

const unselectedBodyType = body_types.filter(
(val) => val.icon !== body_type,
)[0];

const unselectedBodySize = body_sizes.filter(
(val) => val.icon !== body_size,
)[0];

return (
<Window width={390} height={180} theme={'crtblue'}>
<Window.Content className="BodyPicker">
{picker && (
<Modal m={1}>
<TypePicker picker={setPicker} toUse={picker} />
</Modal>
)}
<Stack>
<Stack.Item>
<Stack vertical>
<Stack.Item>
<DmIcon
icon={icon}
icon_state={`${skin_color}_torso_${body_size}_${body_type}`}
width={'128px'}
mt={-5}
/>
</Stack.Item>
<Stack width="100%" fill mt={-4} justify="space-around">
<Stack.Item>
<Button width={'4em'} height={'4em'}>
<Tooltip
content={'Change Body Type'}
position="bottom-start"
>
<Box
position="relative"
onClick={() => setPicker('type')}
>
<DmIcon
position="relative"
icon={icon}
icon_state={`${skin_color}_torso_${body_size}_${unselectedBodyType.icon}`}
width={'80px'}
right={'22px'}
bottom={'18px'}
/>
</Box>
</Tooltip>
</Button>
</Stack.Item>
<Stack.Item>
<Button width={'4em'} height={'4em'}>
<Tooltip
content={'Change Body Size'}
position="bottom-start"
>
<Box
position="relative"
onClick={() => setPicker('size')}
>
<DmIcon
position="relative"
icon={icon}
icon_state={`${skin_color}_torso_${unselectedBodySize.icon}_${body_type}`}
width={'80px'}
right={'22px'}
bottom={'18px'}
/>
</Box>
</Tooltip>
</Button>
</Stack.Item>
</Stack>
</Stack>
</Stack.Item>
<Stack.Item>
<ColorOptions />
</Stack.Item>
</Stack>
</Window.Content>
</Window>
);
};

const TypePicker = (props: {
readonly picker: (_) => void;
readonly toUse: 'type' | 'size';
}) => {
const { data, act } = useBackend<PickerData>();

const { picker, toUse } = props;

const { body_type, body_types, skin_color, body_size, body_sizes, icon } =
data;

const toIterate = toUse === 'type' ? body_types : body_sizes;

const active = toUse === 'type' ? body_type : body_size;

return (
<Stack>
{toIterate.map((type) => (
<Stack.Item key={type.name}>
<Tooltip content={type.name}>
<Box
onClick={() => {
picker(undefined);
act(toUse, { name: type.name });
}}
position="relative"
className={`typePicker ${active === type.icon ? 'active' : ''}`}
>
<DmIcon
icon={icon}
icon_state={
toUse === 'type'
? `${skin_color}_torso_${body_size}_${type.icon}`
: `${skin_color}_torso_${type.icon}_${body_type}`
}
width={'80px'}
/>
</Box>
</Tooltip>
</Stack.Item>
))}
</Stack>
);
};

const ColorOptions = () => {
const { data, act } = useBackend<PickerData>();

const { skin_color, skin_colors } = data;

return (
<Stack wrap={'wrap'} width={'250px'}>
{skin_colors.map((color) => (
<Stack.Item key={color.name} className="colorPickerContainer">
<ColorBox
color={color.color}
p={3.5}
mt={0.5}
onClick={() => act('color', { name: color.name })}
className={`colorPicker ${skin_color === color.icon ? 'active' : ''}`}
/>
</Stack.Item>
))}
</Stack>
);
};
19 changes: 19 additions & 0 deletions tgui/packages/tgui/styles/interfaces/BodyPicker.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.theme-crtblue {
.BodyPicker {
.Stack--horizontal > .colorPickerContainer:first-of-type {
margin-left: 6px;
}

.typePicker {
border: 1px dotted #8ac8ff;
}

.typePicker.active {
border: 1px solid #8ac8ff;
}

.colorPicker.active {
outline: solid 2px #8ac8ff;
}
}
}
1 change: 1 addition & 0 deletions tgui/packages/tgui/styles/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
@include meta.load-css('./components/Tooltip.scss');

// Interfaces
@include meta.load-css('./interfaces/BodyPicker.scss');
@include meta.load-css('./interfaces/Changelog.scss');
@include meta.load-css('./interfaces/ListInput.scss');
@include meta.load-css('./interfaces/CasSim.scss');
Expand Down
5 changes: 5 additions & 0 deletions tgui/packages/tgui/styles/themes/crt.scss
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ $background-radial-opacity: 0.2 !default;
)
);

@include meta.load-css(
'../components/Modal.scss',
$with: ('background-color': base.$color-bg)
);

.Layout__content {
background-image: none;
background: radial-gradient(
Expand Down

0 comments on commit c7e20e4

Please sign in to comment.