@@ -23,6 +23,11 @@
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
|
||||
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="_White\Wizard\Mirror\WizardMirrorWindow.cs">
|
||||
<DependentUpon>WizardMirrorWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
|
||||
<Import Project="..\RobustToolbox\MSBuild\XamlIL.targets" />
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
using Content.Shared._White.Wizard.Mirror;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._White.Wizard.Mirror;
|
||||
|
||||
public sealed class WizardMirrorBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private WizardMirrorWindow? _window;
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new(_prototypeManager);
|
||||
|
||||
_window.OnSave += Save;
|
||||
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
private void Save(HumanoidCharacterProfile profile)
|
||||
{
|
||||
SendMessage(new WizardMirrorSave(profile));
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not WizardMirrorUiState data || _window == null)
|
||||
return;
|
||||
|
||||
_window.UpdateState(data);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
if (_window != null)
|
||||
_window.OnClose -= Close;
|
||||
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
692
Content.Client/_White/Wizard/Mirror/WizardMirrorWindow.cs
Normal file
692
Content.Client/_White/Wizard/Mirror/WizardMirrorWindow.cs
Normal file
@@ -0,0 +1,692 @@
|
||||
using System.Linq;
|
||||
using Content.Client._White.Sponsors;
|
||||
using Content.Client.Humanoid;
|
||||
using Content.Shared._White.Wizard.Mirror;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Content.Shared._White.TTS;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._White.Wizard.Mirror;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class WizardMirrorWindow : DefaultWindow
|
||||
{
|
||||
private LineEdit NameEdit => CNameEdit;
|
||||
|
||||
private Button SaveButton => CSaveButton;
|
||||
public Action<HumanoidCharacterProfile>? OnSave;
|
||||
|
||||
private OptionButton SexButton => CSexButton;
|
||||
|
||||
private LineEdit AgeEdit => CAgeEdit;
|
||||
|
||||
private OptionButton GenderButton => CPronounsButton;
|
||||
|
||||
private OptionButton VoiceButton => CVoiceButton;
|
||||
|
||||
private OptionButton BodyTypesButton => CBodyTypesButton;
|
||||
|
||||
private OptionButton SpeciesButton => CSpeciesButton;
|
||||
|
||||
private Slider SkinColorSlider => CSkin;
|
||||
private BoxContainer RgbSkinColorContainer => CRgbSkinColorContainer;
|
||||
private ColorSelectorSliders _rgbSkinColorSelector;
|
||||
|
||||
// Hair
|
||||
private SingleMarkingPicker HairPicker => CHairStylePicker;
|
||||
private SingleMarkingPicker FacialHairPicker => CFacialHairPicker;
|
||||
|
||||
private EyeColorPicker EyesPicker => CEyeColorPicker;
|
||||
|
||||
private bool _isDirty;
|
||||
public HumanoidCharacterProfile? Profile;
|
||||
|
||||
private readonly MarkingManager _markingManager;
|
||||
private readonly IPrototypeManager _prototypeManager;
|
||||
|
||||
private List<BodyTypePrototype> _bodyTypesList = new();
|
||||
|
||||
private readonly List<SpeciesPrototype> _speciesList;
|
||||
|
||||
private List<TTSVoicePrototype> _voiceList = default!;
|
||||
private const string AnySexVoiceProto = "SponsorAnySexVoices";
|
||||
|
||||
public WizardMirrorWindow(IPrototypeManager prototypeManager)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
_markingManager = IoCManager.Resolve<MarkingManager>();
|
||||
_prototypeManager = prototypeManager;
|
||||
|
||||
Profile ??= HumanoidCharacterProfile.RandomWithSpecies();
|
||||
|
||||
SaveButton.OnPressed += _ => OnSave!(Profile);
|
||||
|
||||
_voiceList = _prototypeManager.EnumeratePrototypes<TTSVoicePrototype>().Where(o => o.RoundStart).ToList();
|
||||
|
||||
#region Voice
|
||||
|
||||
VoiceButton.OnItemSelected += args =>
|
||||
{
|
||||
VoiceButton.SelectId(args.Id);
|
||||
SetVoice(_voiceList[args.Id].ID);
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Name
|
||||
|
||||
NameEdit.OnTextChanged += args => { SetName(args.Text); };
|
||||
|
||||
#endregion
|
||||
|
||||
#region Age
|
||||
|
||||
AgeEdit.OnTextChanged += args =>
|
||||
{
|
||||
if (!int.TryParse(args.Text, out var newAge))
|
||||
return;
|
||||
SetAge(newAge);
|
||||
};
|
||||
|
||||
#endregion Age
|
||||
|
||||
#region Sex
|
||||
|
||||
SexButton.OnItemSelected += args =>
|
||||
{
|
||||
SexButton.SelectId(args.Id);
|
||||
SetSex((Sex) args.Id);
|
||||
};
|
||||
|
||||
#endregion Sex
|
||||
|
||||
#region Gender
|
||||
|
||||
GenderButton.AddItem(Loc.GetString("humanoid-profile-editor-pronouns-male-text"), (int) Gender.Male);
|
||||
GenderButton.AddItem(Loc.GetString("humanoid-profile-editor-pronouns-female-text"), (int) Gender.Female);
|
||||
GenderButton.AddItem(Loc.GetString("humanoid-profile-editor-pronouns-epicene-text"), (int) Gender.Epicene);
|
||||
GenderButton.AddItem(Loc.GetString("humanoid-profile-editor-pronouns-neuter-text"), (int) Gender.Neuter);
|
||||
|
||||
GenderButton.OnItemSelected += args =>
|
||||
{
|
||||
GenderButton.SelectId(args.Id);
|
||||
SetGender((Gender) args.Id);
|
||||
};
|
||||
|
||||
#endregion Gender
|
||||
|
||||
#region Body Type
|
||||
|
||||
BodyTypesButton.OnItemSelected += OnBodyTypeSelected;
|
||||
|
||||
UpdateBodyTypes();
|
||||
|
||||
#endregion Body Type
|
||||
|
||||
#region Skin
|
||||
|
||||
|
||||
SkinColorSlider.OnValueChanged += _ =>
|
||||
{
|
||||
OnSkinColorOnValueChanged();
|
||||
};
|
||||
|
||||
RgbSkinColorContainer.AddChild(_rgbSkinColorSelector = new ColorSelectorSliders());
|
||||
_rgbSkinColorSelector.OnColorChanged += _ =>
|
||||
{
|
||||
OnSkinColorOnValueChanged();
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Species
|
||||
|
||||
_speciesList = prototypeManager.EnumeratePrototypes<SpeciesPrototype>().Where(o => o.RoundStart).ToList();
|
||||
|
||||
for (var i = 0; i < _speciesList.Count; i++)
|
||||
{
|
||||
var specie = _speciesList[i];
|
||||
var name = Loc.GetString(specie.Name);
|
||||
|
||||
SpeciesButton.AddItem(name, i);
|
||||
}
|
||||
|
||||
SpeciesButton.OnItemSelected += args =>
|
||||
{
|
||||
SpeciesButton.SelectId(args.Id);
|
||||
SetSpecies(_speciesList[args.Id].ID);
|
||||
UpdateHairPickers();
|
||||
OnSkinColorOnValueChanged();
|
||||
};
|
||||
|
||||
#endregion Species
|
||||
|
||||
#region Hair
|
||||
|
||||
HairPicker.OnMarkingSelect += newStyle =>
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithHairStyleName(newStyle.id));
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
HairPicker.OnColorChanged += newColor =>
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithHairColor(newColor.marking.MarkingColors[0]));
|
||||
UpdateCMarkingsHair();
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
FacialHairPicker.OnMarkingSelect += newStyle =>
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithFacialHairStyleName(newStyle.id));
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
FacialHairPicker.OnColorChanged += newColor =>
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithFacialHairColor(newColor.marking.MarkingColors[0]));
|
||||
UpdateCMarkingsFacialHair();
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
HairPicker.OnSlotRemove += _ =>
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithHairStyleName(HairStyles.DefaultHairStyle)
|
||||
);
|
||||
UpdateHairPickers();
|
||||
UpdateCMarkingsHair();
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
FacialHairPicker.OnSlotRemove += _ =>
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithFacialHairStyleName(HairStyles.DefaultFacialHairStyle)
|
||||
);
|
||||
UpdateHairPickers();
|
||||
UpdateCMarkingsFacialHair();
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
HairPicker.OnSlotAdd += delegate
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
|
||||
var hair = _markingManager.MarkingsByCategoryAndSpecies(MarkingCategories.Hair, Profile.Species)
|
||||
.Keys
|
||||
.FirstOrDefault();
|
||||
|
||||
if (string.IsNullOrEmpty(hair))
|
||||
return;
|
||||
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithHairStyleName(hair)
|
||||
);
|
||||
|
||||
UpdateHairPickers();
|
||||
UpdateCMarkingsHair();
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
FacialHairPicker.OnSlotAdd += delegate
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
|
||||
var hair = _markingManager.MarkingsByCategoryAndSpecies(MarkingCategories.FacialHair, Profile.Species)
|
||||
.Keys
|
||||
.FirstOrDefault();
|
||||
|
||||
if (string.IsNullOrEmpty(hair))
|
||||
return;
|
||||
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithFacialHairStyleName(hair)
|
||||
);
|
||||
|
||||
UpdateHairPickers();
|
||||
UpdateCMarkingsFacialHair();
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
#endregion Hair
|
||||
|
||||
#region Eyes
|
||||
|
||||
EyesPicker.OnEyeColorPicked += newColor =>
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithEyeColor(newColor));
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
#endregion Eyes
|
||||
}
|
||||
|
||||
#region Set
|
||||
|
||||
private void SetAge(int newAge)
|
||||
{
|
||||
Profile = Profile?.WithAge(newAge);
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void SetSex(Sex newSex)
|
||||
{
|
||||
Profile = Profile?.WithSex(newSex);
|
||||
switch (newSex)
|
||||
{
|
||||
case Sex.Male:
|
||||
Profile = Profile?.WithGender(Gender.Male);
|
||||
break;
|
||||
case Sex.Female:
|
||||
Profile = Profile?.WithGender(Gender.Female);
|
||||
break;
|
||||
default:
|
||||
Profile = Profile?.WithGender(Gender.Epicene);
|
||||
break;
|
||||
}
|
||||
UpdateGenderControls();
|
||||
UpdateTtsVoicesControls();
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void SetVoice(string newVoice)
|
||||
{
|
||||
Profile = Profile?.WithVoice(newVoice);
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void SetGender(Gender newGender)
|
||||
{
|
||||
Profile = Profile?.WithGender(newGender);
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void SetSpecies(string newSpecies)
|
||||
{
|
||||
Profile = Profile?.WithSpecies(newSpecies);
|
||||
OnSkinColorOnValueChanged();
|
||||
UpdateSexControls();
|
||||
UpdateBodyTypes();
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void SetName(string newName)
|
||||
{
|
||||
Profile = Profile?.WithName(newName);
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void SetBodyType(string newBodyType)
|
||||
{
|
||||
Profile = Profile?.WithBodyType(newBodyType);
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Update
|
||||
|
||||
private void UpdateSaveButton()
|
||||
{
|
||||
SaveButton.Disabled = Profile is null || !IsDirty;
|
||||
}
|
||||
|
||||
private void UpdateNamesEdit()
|
||||
{
|
||||
NameEdit.Text = Profile?.Name ?? "";
|
||||
}
|
||||
|
||||
private void UpdateGenderControls()
|
||||
{
|
||||
if (Profile == null)
|
||||
return;
|
||||
|
||||
GenderButton.SelectId((int) Profile.Gender);
|
||||
}
|
||||
|
||||
private void UpdateTtsVoicesControls()
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
|
||||
var sponsorsManager = IoCManager.Resolve<SponsorsManager>();
|
||||
|
||||
VoiceButton.Clear();
|
||||
|
||||
var firstVoiceChoiceId = 1;
|
||||
for (var i = 0; i < _voiceList.Count; i++)
|
||||
{
|
||||
var voice = _voiceList[i];
|
||||
if (!HumanoidCharacterProfile.CanHaveVoice(voice, Profile.Sex))
|
||||
{
|
||||
if (!sponsorsManager.TryGetInfo(out var sponsorInfo)
|
||||
|| !sponsorInfo.AllowedMarkings.Contains(AnySexVoiceProto))
|
||||
continue;
|
||||
}
|
||||
|
||||
var name = Loc.GetString(voice.Name);
|
||||
VoiceButton.AddItem(name, i);
|
||||
|
||||
if (firstVoiceChoiceId == 1)
|
||||
firstVoiceChoiceId = i;
|
||||
|
||||
if (voice.SponsorOnly &&
|
||||
sponsorsManager.TryGetInfo(out var sponsor) &&
|
||||
!sponsor.AllowedMarkings.Contains(voice.ID))
|
||||
{
|
||||
VoiceButton.SetItemDisabled(i, true);
|
||||
}
|
||||
}
|
||||
|
||||
var voiceChoiceId = _voiceList.FindIndex(x => x.ID == Profile.Voice);
|
||||
if (!VoiceButton.TrySelectId(voiceChoiceId) &&
|
||||
VoiceButton.TrySelectId(firstVoiceChoiceId))
|
||||
{
|
||||
SetVoice(_voiceList[firstVoiceChoiceId].ID);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSexControls()
|
||||
{
|
||||
if (Profile == null)
|
||||
return;
|
||||
|
||||
SexButton.Clear();
|
||||
|
||||
var sexes = new List<Sex>();
|
||||
|
||||
if (!_prototypeManager.TryIndex<SpeciesPrototype>(Profile.Species, out var speciesProto))
|
||||
sexes.Add(Sex.Unsexed);
|
||||
else
|
||||
sexes.AddRange(speciesProto.Sexes);
|
||||
|
||||
foreach (var sex in sexes)
|
||||
{
|
||||
SexButton.AddItem(Loc.GetString($"humanoid-profile-editor-sex-{sex.ToString().ToLower()}-text"), (int) sex);
|
||||
}
|
||||
|
||||
if (sexes.Contains(Profile.Sex))
|
||||
SexButton.SelectId((int) Profile.Sex);
|
||||
else
|
||||
SexButton.SelectId((int) sexes[0]);
|
||||
}
|
||||
|
||||
private void UpdateBodyTypes()
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
|
||||
BodyTypesButton.Clear();
|
||||
var species = _prototypeManager.Index<SpeciesPrototype>(Profile.Species);
|
||||
var sex = Profile.Sex;
|
||||
_bodyTypesList = EntitySystem.Get<HumanoidAppearanceSystem>().GetValidBodyTypes(species, sex);
|
||||
|
||||
for (var i = 0; i < _bodyTypesList.Count; i++)
|
||||
{
|
||||
BodyTypesButton.AddItem(Loc.GetString(_bodyTypesList[i].Name), i);
|
||||
}
|
||||
|
||||
if (!_bodyTypesList.Select(proto => proto.ID).Contains(Profile.BodyType.Id))
|
||||
SetBodyType(_bodyTypesList.First().ID);
|
||||
|
||||
BodyTypesButton.Select(_bodyTypesList.FindIndex(x => x.ID == Profile.BodyType));
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void UpdateHairPickers()
|
||||
{
|
||||
if (Profile == null)
|
||||
return;
|
||||
|
||||
var hairMarking = Profile.Appearance.HairStyleId switch
|
||||
{
|
||||
HairStyles.DefaultHairStyle => new List<Marking>(),
|
||||
_ => new List<Marking> { new(Profile.Appearance.HairStyleId, new List<Color> { Profile.Appearance.HairColor }) },
|
||||
};
|
||||
|
||||
var facialHairMarking = Profile.Appearance.FacialHairStyleId switch
|
||||
{
|
||||
HairStyles.DefaultFacialHairStyle => new List<Marking>(),
|
||||
_ => new List<Marking> { new(Profile.Appearance.FacialHairStyleId, new List<Color> { Profile.Appearance.FacialHairColor }) },
|
||||
};
|
||||
|
||||
HairPicker.UpdateData(hairMarking, Profile.Species, 1);
|
||||
FacialHairPicker.UpdateData(facialHairMarking, Profile.Species, 1);
|
||||
}
|
||||
|
||||
private void UpdateCMarkingsFacialHair()
|
||||
{
|
||||
if (Profile == null)
|
||||
return;
|
||||
|
||||
Color? facialHairColor = null;
|
||||
if ( Profile.Appearance.FacialHairStyleId != HairStyles.DefaultFacialHairStyle &&
|
||||
_markingManager.Markings.TryGetValue(Profile.Appearance.FacialHairStyleId, out var facialHairProto)
|
||||
)
|
||||
{
|
||||
if (_markingManager.CanBeApplied(Profile.Species, Profile.Sex, facialHairProto, _prototypeManager))
|
||||
{
|
||||
facialHairColor = _markingManager.MustMatchSkin(Profile.BodyType, HumanoidVisualLayers.Hair, out _, _prototypeManager) ? Profile.Appearance.SkinColor : Profile.Appearance.FacialHairColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCMarkingsHair()
|
||||
{
|
||||
if (Profile == null)
|
||||
return;
|
||||
|
||||
// hair color
|
||||
Color? hairColor = null;
|
||||
if ( Profile.Appearance.HairStyleId != HairStyles.DefaultHairStyle &&
|
||||
_markingManager.Markings.TryGetValue(Profile.Appearance.HairStyleId, out var hairProto)
|
||||
)
|
||||
{
|
||||
if (_markingManager.CanBeApplied(Profile.Species, Profile.Sex, hairProto, _prototypeManager))
|
||||
{
|
||||
hairColor = _markingManager.MustMatchSkin(Profile.BodyType, HumanoidVisualLayers.Hair, out _, _prototypeManager)
|
||||
? Profile.Appearance.SkinColor
|
||||
: Profile.Appearance.HairColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSkinColor()
|
||||
{
|
||||
if (Profile == null)
|
||||
return;
|
||||
|
||||
var skin = _prototypeManager.Index<SpeciesPrototype>(Profile.Species).SkinColoration;
|
||||
|
||||
switch (skin)
|
||||
{
|
||||
case HumanoidSkinColor.HumanToned:
|
||||
{
|
||||
if (!SkinColorSlider.Visible)
|
||||
{
|
||||
SkinColorSlider.Visible = true;
|
||||
_rgbSkinColorSelector.Visible = false;
|
||||
}
|
||||
|
||||
SkinColorSlider.Value = SkinColor.HumanSkinToneFromColor(Profile.Appearance.SkinColor);
|
||||
|
||||
break;
|
||||
}
|
||||
case HumanoidSkinColor.Hues:
|
||||
{
|
||||
if (!_rgbSkinColorSelector.Visible)
|
||||
{
|
||||
SkinColorSlider.Visible = false;
|
||||
_rgbSkinColorSelector.Visible = true;
|
||||
}
|
||||
|
||||
// set the RGB values to the direct values otherwise
|
||||
_rgbSkinColorSelector.Color = Profile.Appearance.SkinColor;
|
||||
break;
|
||||
}
|
||||
case HumanoidSkinColor.TintedHues:
|
||||
{
|
||||
if (!_rgbSkinColorSelector.Visible)
|
||||
{
|
||||
SkinColorSlider.Visible = false;
|
||||
_rgbSkinColorSelector.Visible = true;
|
||||
}
|
||||
|
||||
// set the RGB values to the direct values otherwise
|
||||
_rgbSkinColorSelector.Color = Profile.Appearance.SkinColor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSpecies()
|
||||
{
|
||||
if (Profile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_speciesList.Exists(x => x.ID == Profile.Species))
|
||||
{
|
||||
SpeciesButton.Select(0);
|
||||
return;
|
||||
}
|
||||
|
||||
SpeciesButton.Select(_speciesList.FindIndex(x => x.ID == Profile.Species));
|
||||
}
|
||||
|
||||
private void UpdateAgeEdit()
|
||||
{
|
||||
AgeEdit.Text = Profile?.Age.ToString() ?? "";
|
||||
}
|
||||
|
||||
private void UpdateEyePickers()
|
||||
{
|
||||
if (Profile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EyesPicker.SetData(Profile.Appearance.EyeColor);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void OnSkinColorOnValueChanged()
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
|
||||
var skin = _prototypeManager.Index<SpeciesPrototype>(Profile.Species).SkinColoration;
|
||||
|
||||
switch (skin)
|
||||
{
|
||||
case HumanoidSkinColor.HumanToned:
|
||||
{
|
||||
if (!SkinColorSlider.Visible)
|
||||
{
|
||||
SkinColorSlider.Visible = true;
|
||||
RgbSkinColorContainer.Visible = false;
|
||||
}
|
||||
|
||||
var color = SkinColor.HumanSkinTone((int) SkinColorSlider.Value);
|
||||
|
||||
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));//
|
||||
break;
|
||||
}
|
||||
case HumanoidSkinColor.Hues:
|
||||
{
|
||||
if (!RgbSkinColorContainer.Visible)
|
||||
{
|
||||
SkinColorSlider.Visible = false;
|
||||
RgbSkinColorContainer.Visible = true;
|
||||
}
|
||||
|
||||
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(_rgbSkinColorSelector.Color));
|
||||
break;
|
||||
}
|
||||
case HumanoidSkinColor.TintedHues:
|
||||
{
|
||||
if (!RgbSkinColorContainer.Visible)
|
||||
{
|
||||
SkinColorSlider.Visible = false;
|
||||
RgbSkinColorContainer.Visible = true;
|
||||
}
|
||||
|
||||
var color = SkinColor.TintedHues(_rgbSkinColorSelector.Color);
|
||||
|
||||
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void OnBodyTypeSelected(OptionButton.ItemSelectedEventArgs args)
|
||||
{
|
||||
args.Button.SelectId(args.Id);
|
||||
SetBodyType(_bodyTypesList[args.Id].ID);
|
||||
}
|
||||
|
||||
private bool IsDirty
|
||||
{
|
||||
get => _isDirty;
|
||||
set
|
||||
{
|
||||
_isDirty = value;
|
||||
UpdateSaveButton();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateState(WizardMirrorUiState state)
|
||||
{
|
||||
Profile = state.Profile;
|
||||
|
||||
UpdateNamesEdit();
|
||||
UpdateSexControls();
|
||||
UpdateGenderControls();
|
||||
UpdateSkinColor();
|
||||
UpdateSpecies();
|
||||
UpdateAgeEdit();
|
||||
UpdateEyePickers();
|
||||
UpdateSaveButton();
|
||||
UpdateHairPickers();
|
||||
UpdateCMarkingsHair();
|
||||
UpdateCMarkingsFacialHair();
|
||||
UpdateTtsVoicesControls();
|
||||
UpdateBodyTypes();
|
||||
}
|
||||
}
|
||||
85
Content.Client/_White/Wizard/Mirror/WizardMirrorWindow.xaml
Normal file
85
Content.Client/_White/Wizard/Mirror/WizardMirrorWindow.xaml
Normal file
@@ -0,0 +1,85 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:prefUi="clr-namespace:Content.Client.Preferences.UI"
|
||||
xmlns:humanoid="clr-namespace:Content.Client.Humanoid"
|
||||
Title="{Loc 'magic-mirror-window-title'}"
|
||||
MinSize="600 400">
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal" SeparationOverride="10">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-name-label'}" />
|
||||
<LineEdit Name="CNameEdit" MinSize="200 0" VerticalAlignment="Center" Margin="5 0 0 0" />
|
||||
<Button Name="CSaveButton" Text="{Loc 'humanoid-profile-editor-save-button'}" HorizontalAlignment="Center" Margin="5 0 0 0"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" SeparationOverride="10">
|
||||
<!-- Sex -->
|
||||
<prefUi:HighlightedContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-sex-label'}" />
|
||||
<OptionButton Name="CSexButton" />
|
||||
</BoxContainer>
|
||||
</prefUi:HighlightedContainer>
|
||||
<!-- Body Type -->
|
||||
<prefUi:HighlightedContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-body-type-label'}"></Label>
|
||||
<OptionButton Name="CBodyTypesButton" />
|
||||
</BoxContainer>
|
||||
</prefUi:HighlightedContainer>
|
||||
<!-- Age -->
|
||||
<prefUi:HighlightedContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-age-label'}" />
|
||||
<LineEdit Name="CAgeEdit" MinSize="40 0" />
|
||||
</BoxContainer>
|
||||
</prefUi:HighlightedContainer>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" SeparationOverride="10">
|
||||
<!-- Gender -->
|
||||
<prefUi:HighlightedContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-pronouns-label'}" />
|
||||
<OptionButton Name="CPronounsButton" />
|
||||
</BoxContainer>
|
||||
</prefUi:HighlightedContainer>
|
||||
<!-- Voice -->
|
||||
<prefUi:HighlightedContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-voice-label'}" />
|
||||
<OptionButton Name="CVoiceButton" />
|
||||
</BoxContainer>
|
||||
</prefUi:HighlightedContainer>
|
||||
<!-- Species -->
|
||||
<prefUi:HighlightedContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-species-label'}" />
|
||||
<Control HorizontalExpand="True"/>
|
||||
<OptionButton Name="CSpeciesButton" />
|
||||
</BoxContainer>
|
||||
</prefUi:HighlightedContainer>
|
||||
</BoxContainer>
|
||||
<!-- Skin -->
|
||||
<prefUi:HighlightedContainer>
|
||||
<BoxContainer HorizontalExpand="True" Orientation="Vertical">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-skin-color-label'}" />
|
||||
<Slider HorizontalExpand="True" Name="CSkin" MinValue="0" MaxValue="100" Value="20" />
|
||||
<BoxContainer Name="CRgbSkinColorContainer" Visible="False" Orientation="Vertical" HorizontalExpand="True"></BoxContainer>
|
||||
</BoxContainer>
|
||||
</prefUi:HighlightedContainer>
|
||||
<!-- Eyes -->
|
||||
<prefUi:HighlightedContainer>
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-eyes-label'}" />
|
||||
<humanoid:EyeColorPicker Name="CEyeColorPicker" />
|
||||
</BoxContainer>
|
||||
</prefUi:HighlightedContainer>
|
||||
<!-- Hair -->
|
||||
<prefUi:HighlightedContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<humanoid:SingleMarkingPicker Name="CHairStylePicker" Category="Hair" />
|
||||
<humanoid:SingleMarkingPicker Name="CFacialHairPicker" Category="FacialHair" />
|
||||
</BoxContainer>
|
||||
</prefUi:HighlightedContainer>
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</DefaultWindow>
|
||||
@@ -357,4 +357,4 @@ public sealed class MagicMirrorSystem : EntitySystem
|
||||
{
|
||||
ent.Comp.Target = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
137
Content.Server/_White/Wizard/Mirror/WizardMirrorSystem.cs
Normal file
137
Content.Server/_White/Wizard/Mirror/WizardMirrorSystem.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using Content.Server.Humanoid;
|
||||
using Content.Server.IdentityManagement;
|
||||
using Content.Shared._White.Wizard.Mirror;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.UserInterface;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server._White.Wizard.Mirror;
|
||||
|
||||
public sealed class WizardMirrorSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||
[Dependency] private readonly IdentitySystem _identity = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<WizardMirrorComponent, ActivatableUIOpenAttemptEvent>(OnOpenUIAttempt);
|
||||
|
||||
Subs.BuiEvents<WizardMirrorComponent>(WizardMirrorUiKey.Key,
|
||||
subs =>
|
||||
{
|
||||
subs.Event<BoundUIClosedEvent>(OnUIClosed);
|
||||
subs.Event<WizardMirrorSave>(OnSave);
|
||||
});
|
||||
|
||||
SubscribeLocalEvent<WizardMirrorComponent, InteractHandEvent>(OnInteractHand);
|
||||
SubscribeLocalEvent<WizardMirrorComponent, AfterInteractEvent>(OnMagicMirrorInteract);
|
||||
|
||||
SubscribeLocalEvent<WizardMirrorComponent, BoundUserInterfaceCheckRangeEvent>(OnRangeCheck);
|
||||
}
|
||||
|
||||
private void OnOpenUIAttempt(EntityUid uid, WizardMirrorComponent mirror, ActivatableUIOpenAttemptEvent args)
|
||||
{
|
||||
if (!HasComp<HumanoidAppearanceComponent>(args.User))
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private static void OnUIClosed(Entity<WizardMirrorComponent> ent, ref BoundUIClosedEvent args)
|
||||
{
|
||||
ent.Comp.Target = null;
|
||||
}
|
||||
|
||||
private void OnSave(EntityUid uid, WizardMirrorComponent component, WizardMirrorSave args)
|
||||
{
|
||||
if (!TryComp(component.Target, out HumanoidAppearanceComponent? humanoid) || !string.IsNullOrEmpty(humanoid.Initial))
|
||||
return;
|
||||
|
||||
_humanoid.LoadProfile(component.Target.Value, args.Profile, humanoid);
|
||||
_metaData.SetEntityName(component.Target.Value, args.Profile.Name);
|
||||
_identity.QueueIdentityUpdate(component.Target.Value);
|
||||
}
|
||||
|
||||
private void OnInteractHand(EntityUid uid, WizardMirrorComponent component, ref InteractHandEvent args)
|
||||
{
|
||||
UpdateInterface(uid, args.User, component);
|
||||
}
|
||||
|
||||
private void OnMagicMirrorInteract(EntityUid uid, WizardMirrorComponent component, ref AfterInteractEvent args)
|
||||
{
|
||||
if (!args.CanReach || args.Target == null)
|
||||
return;
|
||||
|
||||
if (!TryComp<ActorComponent>(args.User, out var actor))
|
||||
return;
|
||||
|
||||
if (!_uiSystem.TryOpen(uid, WizardMirrorUiKey.Key, actor.PlayerSession))
|
||||
return;
|
||||
|
||||
UpdateInterface(uid, args.Target.Value, component);
|
||||
}
|
||||
|
||||
private void OnRangeCheck(EntityUid uid, WizardMirrorComponent component, ref BoundUserInterfaceCheckRangeEvent args)
|
||||
{
|
||||
component.Target ??= args.Player.AttachedEntity;
|
||||
|
||||
if (!component.Target.HasValue || !_interaction.InRangeUnobstructed(uid, component.Target!.Value, range: 2f, CollisionGroup.None))
|
||||
args.Result = BoundUserInterfaceRangeResult.Fail;
|
||||
}
|
||||
|
||||
private void UpdateInterface(EntityUid mirrorUid, EntityUid targetUid, WizardMirrorComponent component)
|
||||
{
|
||||
if (!TryComp<HumanoidAppearanceComponent>(targetUid, out var humanoid) ||
|
||||
!TryComp<MetaDataComponent>(targetUid, out var meta))
|
||||
return;
|
||||
|
||||
var hair = humanoid.MarkingSet.TryGetCategory(MarkingCategories.Hair, out var hairMarkings)
|
||||
? new List<Marking>(hairMarkings)[0]
|
||||
: null;
|
||||
|
||||
var facialHair = humanoid.MarkingSet.TryGetCategory(MarkingCategories.FacialHair, out var facialHairMarkings)
|
||||
? new List<Marking>(facialHairMarkings)[0]
|
||||
: null;
|
||||
|
||||
var profile = HumanoidCharacterProfile.RandomWithSpecies(humanoid.Species)
|
||||
.WithAge(humanoid.Age)
|
||||
.WithGender(humanoid.Gender)
|
||||
.WithName(meta.EntityName)
|
||||
.WithSex(humanoid.Sex)
|
||||
.WithVoice(humanoid.Voice)
|
||||
.WithBodyType(humanoid.BodyType);
|
||||
|
||||
profile = profile.WithCharacterAppearance(
|
||||
profile.WithCharacterAppearance(
|
||||
profile.Appearance.WithSkinColor(humanoid.SkinColor))
|
||||
.Appearance.WithEyeColor(humanoid.EyeColor));
|
||||
|
||||
if (hair != null)
|
||||
{
|
||||
profile = profile.WithCharacterAppearance(
|
||||
profile.WithCharacterAppearance(
|
||||
profile.Appearance.WithHairStyleName(hair.MarkingId))
|
||||
.Appearance.WithHairColor(hair.MarkingColors[0]));
|
||||
}
|
||||
|
||||
if (facialHair != null)
|
||||
{
|
||||
profile = profile.WithCharacterAppearance(
|
||||
profile.WithCharacterAppearance(
|
||||
profile.Appearance.WithFacialHairStyleName(facialHair.MarkingId))
|
||||
.Appearance.WithFacialHairColor(facialHair.MarkingColors[0]));
|
||||
}
|
||||
|
||||
var state = new WizardMirrorUiState(profile);
|
||||
|
||||
component.Target = targetUid;
|
||||
_uiSystem.TrySetUiState(mirrorUid, WizardMirrorUiKey.Key, state);
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,22 @@ public sealed partial class WizardComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool EndRoundOnDeath;
|
||||
|
||||
[DataField]
|
||||
public int MinAge = 90;
|
||||
|
||||
[DataField]
|
||||
public int MaxAge = 170;
|
||||
|
||||
[DataField]
|
||||
public string Hair = "WizardHair";
|
||||
|
||||
[DataField]
|
||||
public string FacialHair = "WizardFacialHair";
|
||||
|
||||
[DataField]
|
||||
public string Color = "WizardHairColor";
|
||||
|
||||
[DataField]
|
||||
public string Name = "WizardNames";
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ using Content.Server.Objectives;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Server.StationEvents.Components;
|
||||
using Content.Shared._White.Antag;
|
||||
using Content.Shared.Dataset;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.NPC.Components;
|
||||
using Content.Shared.Objectives.Components;
|
||||
@@ -55,6 +56,7 @@ public sealed class WizardRuleSystem : GameRuleSystem<WizardRuleComponent>
|
||||
[Dependency] private readonly RoundEndSystem _roundEndSystem = default!;
|
||||
[Dependency] private readonly ObjectivesSystem _objectives = default!;
|
||||
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -137,10 +139,6 @@ public sealed class WizardRuleSystem : GameRuleSystem<WizardRuleComponent>
|
||||
if (!TryComp<WizardSpawnerComponent>(spawner, out var wizardSpawner))
|
||||
return;
|
||||
|
||||
HumanoidCharacterProfile? profile = null;
|
||||
if (TryComp(args.Spawned, out ActorComponent? actor))
|
||||
profile = _prefs.GetPreferences(actor.PlayerSession.UserId).SelectedCharacter as HumanoidCharacterProfile;
|
||||
|
||||
if (!EntityQuery<WizardRuleComponent>().Any())
|
||||
return;
|
||||
|
||||
@@ -150,7 +148,7 @@ public sealed class WizardRuleSystem : GameRuleSystem<WizardRuleComponent>
|
||||
return;
|
||||
}
|
||||
|
||||
SetupWizardEntity(uid, gear, profile, false);
|
||||
SetupWizardEntity(uid, gear, false);
|
||||
}
|
||||
|
||||
private void OnMindAdded(EntityUid uid, WizardComponent component, MindAddedMessage args)
|
||||
@@ -279,25 +277,40 @@ public sealed class WizardRuleSystem : GameRuleSystem<WizardRuleComponent>
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SetupWizardEntity(
|
||||
private HumanoidCharacterProfile SetupWizardEntity(
|
||||
EntityUid mob,
|
||||
StartingGearPrototype gear,
|
||||
HumanoidCharacterProfile? profile,
|
||||
bool endRoundOnDeath)
|
||||
{
|
||||
EnsureComp<WizardComponent>(mob).EndRoundOnDeath = endRoundOnDeath;
|
||||
EnsureComp<WizardComponent>(mob, out var component);
|
||||
component.EndRoundOnDeath = endRoundOnDeath;
|
||||
EnsureComp<GlobalAntagonistComponent>(mob).AntagonistPrototype = "globalAntagonistWizard";
|
||||
|
||||
profile ??= HumanoidCharacterProfile.RandomWithSpecies();
|
||||
var random = IoCManager.Resolve<IRobustRandom>();
|
||||
var profile = HumanoidCharacterProfile.RandomWithSpecies().WithAge(random.Next(component.MinAge, component.MaxAge));
|
||||
|
||||
var color = Color.FromHex(GetRandom(component.Color, "#B5B8B1"));
|
||||
var hair = GetRandom(component.Hair, "HumanHairAfricanPigtails");
|
||||
var facialHair = GetRandom(component.FacialHair, "HumanFacialHairAbe");
|
||||
profile = profile.WithCharacterAppearance(
|
||||
profile.WithCharacterAppearance(
|
||||
profile.WithCharacterAppearance(
|
||||
profile.WithCharacterAppearance(
|
||||
profile.Appearance.WithHairStyleName(hair))
|
||||
.Appearance.WithFacialHairStyleName(facialHair))
|
||||
.Appearance.WithHairColor(color))
|
||||
.Appearance.WithFacialHairColor(color));
|
||||
|
||||
_humanoid.LoadProfile(mob, profile);
|
||||
|
||||
_metaData.SetEntityName(mob, profile.Name);
|
||||
_metaData.SetEntityName(mob, GetRandom(component.Name, ""));
|
||||
|
||||
_stationSpawning.EquipStartingGear(mob, gear);
|
||||
|
||||
_npcFaction.RemoveFaction(mob, "NanoTrasen", false);
|
||||
_npcFaction.AddFaction(mob, "Wizard");
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
private EntityCoordinates WizardSpawnPoint(WizardRuleComponent component)
|
||||
@@ -342,13 +355,7 @@ public sealed class WizardRuleSystem : GameRuleSystem<WizardRuleComponent>
|
||||
//If a session is available, spawn mob and transfer mind into it
|
||||
if (session != null)
|
||||
{
|
||||
var profile =
|
||||
_prefs.GetPreferences(session.UserId).SelectedCharacter as HumanoidCharacterProfile;
|
||||
|
||||
profile ??= HumanoidCharacterProfile.RandomWithSpecies();
|
||||
var name = profile.Name;
|
||||
|
||||
if (!_prototypeManager.TryIndex(profile.Species, out SpeciesPrototype? species))
|
||||
if (!_prototypeManager.TryIndex(SharedHumanoidAppearanceSystem.DefaultSpecies, out SpeciesPrototype? species))
|
||||
{
|
||||
species = _prototypeManager.Index<SpeciesPrototype>(SharedHumanoidAppearanceSystem.DefaultSpecies);
|
||||
}
|
||||
@@ -360,7 +367,7 @@ public sealed class WizardRuleSystem : GameRuleSystem<WizardRuleComponent>
|
||||
return;
|
||||
}
|
||||
|
||||
SetupWizardEntity(mob, gear, profile, true);
|
||||
var name = SetupWizardEntity(mob, gear, true).Name;
|
||||
|
||||
var newMind = _mind.CreateMind(session.UserId, name);
|
||||
_mind.SetUserId(newMind, session.UserId);
|
||||
@@ -442,10 +449,6 @@ public sealed class WizardRuleSystem : GameRuleSystem<WizardRuleComponent>
|
||||
return false;
|
||||
}
|
||||
|
||||
HumanoidCharacterProfile? profile = null;
|
||||
if (TryComp(wizard, out ActorComponent? actor))
|
||||
profile = _prefs.GetPreferences(actor.PlayerSession.UserId).SelectedCharacter as HumanoidCharacterProfile;
|
||||
|
||||
if (giveObjectives)
|
||||
{
|
||||
AddRole(mindId, mind, rule);
|
||||
@@ -457,7 +460,7 @@ public sealed class WizardRuleSystem : GameRuleSystem<WizardRuleComponent>
|
||||
return false;
|
||||
}
|
||||
|
||||
SetupWizardEntity(wizard, gear, profile, false);
|
||||
SetupWizardEntity(wizard, gear, false);
|
||||
|
||||
var spawnpoint = WizardSpawnPoint(rule);
|
||||
var transform = EnsureComp<TransformComponent>(wizard);
|
||||
@@ -465,4 +468,11 @@ public sealed class WizardRuleSystem : GameRuleSystem<WizardRuleComponent>
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private string GetRandom(string list, string ifNull)
|
||||
{
|
||||
return _prototypeManager.TryIndex<DatasetPrototype>(list, out var prototype)
|
||||
? _random.Pick(prototype.Values)
|
||||
: ifNull;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._White.Wizard.Mirror;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum WizardMirrorUiKey : byte
|
||||
{
|
||||
Key
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class WizardMirrorSave(HumanoidCharacterProfile profile) : BoundUserInterfaceMessage
|
||||
{
|
||||
public HumanoidCharacterProfile Profile { get; } = profile;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class WizardMirrorUiState(
|
||||
HumanoidCharacterProfile profile)
|
||||
: BoundUserInterfaceState
|
||||
{
|
||||
public NetEntity Target;
|
||||
|
||||
public HumanoidCharacterProfile Profile = profile;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Content.Shared._White.Wizard.Mirror;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class WizardMirrorComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public EntityUid? Target;
|
||||
}
|
||||
2
Resources/Locale/ru-RU/_white/wizard/mirror.ftl
Normal file
2
Resources/Locale/ru-RU/_white/wizard/mirror.ftl
Normal file
@@ -0,0 +1,2 @@
|
||||
ent-MagicMirror = волшебное зеркало
|
||||
.desc = Свет мой, зеркальце, скажи, да всю правду доложи, я ль робастней всех на свете?
|
||||
@@ -23,24 +23,12 @@ tilemap:
|
||||
entities:
|
||||
- proto: ""
|
||||
entities:
|
||||
- uid: 1
|
||||
components:
|
||||
- type: MetaData
|
||||
name: map 2
|
||||
- type: Transform
|
||||
- type: Map
|
||||
- type: PhysicsMap
|
||||
- type: GridTree
|
||||
- type: MovedGrids
|
||||
- type: Broadphase
|
||||
- type: OccluderTree
|
||||
- type: LoadedMap
|
||||
- uid: 2
|
||||
components:
|
||||
- type: MetaData
|
||||
- type: Transform
|
||||
pos: 0.3842575,0.4217209
|
||||
parent: 1
|
||||
parent: invalid
|
||||
- type: MapGrid
|
||||
chunks:
|
||||
-1,-1:
|
||||
@@ -3093,6 +3081,23 @@ entities:
|
||||
- type: Transform
|
||||
pos: -9.508206,-3.3199291
|
||||
parent: 2
|
||||
- proto: MagicMirror
|
||||
entities:
|
||||
- uid: 1
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 2.5,-8.5
|
||||
parent: 2
|
||||
- uid: 449
|
||||
components:
|
||||
- type: Transform
|
||||
pos: -2.5,1.5
|
||||
parent: 2
|
||||
- uid: 450
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 1.5,1.5
|
||||
parent: 2
|
||||
- proto: MedkitAdvancedFilled
|
||||
entities:
|
||||
- uid: 447
|
||||
@@ -3107,23 +3112,6 @@ entities:
|
||||
- type: Transform
|
||||
pos: -2.3338752,-0.5901818
|
||||
parent: 2
|
||||
- proto: Mirror
|
||||
entities:
|
||||
- uid: 449
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 2.5,-8.5
|
||||
parent: 2
|
||||
- uid: 450
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 1.5,1.5
|
||||
parent: 2
|
||||
- uid: 451
|
||||
components:
|
||||
- type: Transform
|
||||
pos: -2.5,1.5
|
||||
parent: 2
|
||||
- proto: NitrogenCanister
|
||||
entities:
|
||||
- uid: 452
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
- type: entity
|
||||
id: MagicMirror
|
||||
name: magic mirror
|
||||
description: 'Mirror mirror on the wall , who''s the most robust of them all?'
|
||||
components:
|
||||
- type: WallMount
|
||||
- type: Sprite
|
||||
sprite: Structures/Wallmounts/mirror.rsi
|
||||
state: mirror
|
||||
- type: InteractionOutline
|
||||
- type: Clickable
|
||||
- type: Transform
|
||||
anchored: true
|
||||
- type: WizardMirror
|
||||
- type: ActivatableUI
|
||||
key: enum.WizardMirrorUiKey.Key
|
||||
singleUser: true
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
- key: enum.WizardMirrorUiKey.Key
|
||||
type: WizardMirrorBoundUserInterface
|
||||
@@ -0,0 +1,13 @@
|
||||
- type: dataset
|
||||
id: WizardFacialHair
|
||||
values:
|
||||
- HumanFacialHairLongbeard
|
||||
- HumanFacialHairMoonshiner
|
||||
- HumanFacialHairVandyke
|
||||
- HumanFacialHairMartialartist
|
||||
- HumanFacialHairMutton
|
||||
- HumanFacialHairLongbeard
|
||||
- HumanFacialHairDwarf
|
||||
- HumanFacialHairWise
|
||||
- HumanFacialHairWatson
|
||||
- HumanFacialHairChinlessbeard
|
||||
14
Resources/Prototypes/_White/Wizard/Appearance/hair.yml
Normal file
14
Resources/Prototypes/_White/Wizard/Appearance/hair.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
- type: dataset
|
||||
id: WizardHair
|
||||
values:
|
||||
- HumanHair80s
|
||||
- HumanHairFeather
|
||||
- HumanHairBun
|
||||
- HumanHairTopknot
|
||||
- HumanHairBalding
|
||||
- HumanHairBigflattop
|
||||
- HumanHairScully
|
||||
- HumanHairBeehivev2
|
||||
- HumanHairSpikey
|
||||
- HumanHairSwept2
|
||||
- HumanHairGloomyLong
|
||||
13
Resources/Prototypes/_White/Wizard/Appearance/hair_color.yml
Normal file
13
Resources/Prototypes/_White/Wizard/Appearance/hair_color.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
- type: dataset
|
||||
id: WizardHairColor
|
||||
values:
|
||||
- "#B5B8B1"
|
||||
- "#4E5754"
|
||||
- "#A5A5A5"
|
||||
- "#676767"
|
||||
- "#CDCDCD"
|
||||
- "#CCCCCC"
|
||||
- "#7C7C7C"
|
||||
- "#747474"
|
||||
- "#6E6E6E"
|
||||
- "#8F8F8F"
|
||||
13
Resources/Prototypes/_White/Wizard/Appearance/names.yml
Normal file
13
Resources/Prototypes/_White/Wizard/Appearance/names.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
- type: dataset
|
||||
id: WizardNames
|
||||
values:
|
||||
- Всеволод Всезнающий
|
||||
- Варфоломей
|
||||
- Тур'озиф
|
||||
- Джоглот Белый
|
||||
- Дедетер Серый
|
||||
- Хасеалиан
|
||||
- Граф Оганар
|
||||
- Вольфганг Могучий
|
||||
- Чистополк Велекодушный
|
||||
- Грарон Жаждующий
|
||||
Reference in New Issue
Block a user