Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
474dc0a
Added functionality to add miis
patchzyy Apr 11, 2025
27c6eca
Added mii button
patchzyy Apr 12, 2025
a101d58
split
patchzyy Apr 12, 2025
f8603e9
Squashed commit of the following:
patchzyy Apr 12, 2025
cc6b7ab
Mii creator
patchzyy Apr 12, 2025
7d30d5f
you can write miis!
patchzyy Apr 12, 2025
deefcda
bit better mii
patchzyy Apr 13, 2025
0daa0a4
.
patchzyy Apr 14, 2025
df4d841
simpler
patchzyy Apr 15, 2025
403bae3
Selector
patchzyy Apr 15, 2025
e3f98be
Squashed commit of the following:
patchzyy Apr 15, 2025
5089134
.
DirkDoes Apr 15, 2025
29e7705
Update from dev
patchzyy Apr 16, 2025
10d9392
move detailedProfileBox
DirkDoes Apr 16, 2025
03d233a
move detailed Profiel Box aswel
DirkDoes Apr 16, 2025
8dd0393
cursor
DirkDoes Apr 16, 2025
bc7744e
Merged dev into miieditor
patchzyy Apr 17, 2025
79f24f5
License mii swapper
patchzyy Apr 17, 2025
e741cf8
Update GameLicenseService.cs
patchzyy Apr 17, 2025
128f595
Fix miiID
patchzyy Apr 17, 2025
5b77f25
fixed crc check
patchzyy Apr 17, 2025
1a1406b
simplified logic
patchzyy Apr 17, 2025
ad98135
Center miis
patchzyy Apr 17, 2025
f86083d
consistantcy things
DirkDoes Apr 18, 2025
9cff6b7
Merge branch 'dev' into MiiEditor
DirkDoes Apr 18, 2025
7d5e27a
resolve merge isues
DirkDoes Apr 18, 2025
1324e0a
Squashed commit of the following:
patchzyy Apr 18, 2025
16a9749
button plus
patchzyy Apr 18, 2025
7f26964
Duplicate and remove miis
patchzyy Apr 18, 2025
32d3c48
mii
DirkDoes Apr 18, 2025
3fe44c3
Merge branch 'dev' into MiiEditor
DirkDoes Apr 18, 2025
94a633d
snackbar here
DirkDoes Apr 18, 2025
3c39dc1
mi block
DirkDoes Apr 18, 2025
266d5ef
Merge branch 'dev' into MiiEditor
DirkDoes Apr 18, 2025
0ce7d86
mii list page
DirkDoes Apr 18, 2025
71cbcc8
Fix duplocate
patchzyy Apr 19, 2025
e98c38d
.
patchzyy Apr 19, 2025
50cf13e
better mii block
DirkDoes Apr 19, 2025
c540d91
Merge branch 'MiiEditor' of https://github.com/TeamWheelWizard/WheelW…
DirkDoes Apr 19, 2025
06530a4
add service
DirkDoes Apr 19, 2025
f615538
psuh
DirkDoes Apr 19, 2025
3d4ceb6
.
DirkDoes Apr 19, 2025
6878053
.
DirkDoes Apr 19, 2025
3da3983
move thing
DirkDoes Apr 19, 2025
4fe002f
Add Mii
patchzyy Apr 19, 2025
b31d191
Fixed mii generation
patchzyy Apr 19, 2025
862a5b7
dev window mii chanell
DirkDoes Apr 20, 2025
38a98b3
buttons
DirkDoes Apr 20, 2025
aa7ceed
favorite
DirkDoes Apr 20, 2025
6d3dcd9
double check database CRC
patchzyy Apr 20, 2025
97c0d3d
.
patchzyy Apr 21, 2025
b820391
remove the detailed profile box
DirkDoes Apr 21, 2025
26ed1f1
yos
DirkDoes Apr 21, 2025
ddc0867
Mii ID creation coolness
patchzyy Apr 21, 2025
3693faf
.
DirkDoes Apr 21, 2025
056632c
Merge branch 'MiiEditor' of https://github.com/TeamWheelWizard/WheelW…
DirkDoes Apr 21, 2025
2c57dc0
docu
patchzyy Apr 21, 2025
7fbb8f1
Merge branch 'MiiEditor' of https://github.com/TeamWheelWizard/WheelW…
patchzyy Apr 21, 2025
8c0dd3a
Mii save with name
patchzyy Apr 21, 2025
02c8452
first setup popiup
DirkDoes Apr 21, 2025
25563b7
start
DirkDoes Apr 22, 2025
f28290a
Merge branch 'dev' into MiiEditor
DirkDoes Apr 22, 2025
610fc1d
start of the popup
DirkDoes Apr 22, 2025
3f9b649
editor pages
DirkDoes Apr 23, 2025
61c400a
save
DirkDoes Apr 23, 2025
6fbdf24
no save mee
DirkDoes Apr 23, 2025
4e2b488
.
DirkDoes Apr 23, 2025
4466401
refersh button
DirkDoes Apr 23, 2025
585717c
All Pages
patchzyy Apr 24, 2025
8e332a0
changes to that thing
DirkDoes Apr 24, 2025
92bf28d
buttons
DirkDoes Apr 24, 2025
ab26ca8
Squashed commit of the following:
patchzyy Apr 27, 2025
ad93d1d
.
patchzyy Apr 27, 2025
6da215f
buttons
DirkDoes Apr 27, 2025
5794d57
Added icons
patchzyy Apr 27, 2025
89838ab
,
DirkDoes Apr 27, 2025
caa168f
reanme
DirkDoes Apr 27, 2025
fd31054
.
patchzyy Apr 27, 2025
2649333
.
patchzyy Apr 27, 2025
f7648a5
colors
DirkDoes Apr 27, 2025
6e95568
.
DirkDoes Apr 27, 2025
6ebb9c5
beard
patchzyy Apr 27, 2025
b7540f4
colors for goatee and moouth
DirkDoes Apr 27, 2025
75f003b
some new colors
DirkDoes Apr 27, 2025
14ca75e
lower quality, and force generation of rfl_db
patchzyy Apr 28, 2025
51a2ae5
.
patchzyy Apr 28, 2025
304cad2
image colors a bit
DirkDoes Apr 28, 2025
1bb9674
colors
DirkDoes Apr 28, 2025
4ef138e
Moved stuff
patchzyy Apr 29, 2025
c723df0
Merge branch 'dev' into MiiEditor
DirkDoes May 3, 2025
c6c3cd8
fixed all the broken stuff due to the merge conflicts
DirkDoes May 3, 2025
6a1b784
making sure only 1 image requests happen when duplicated miis
DirkDoes May 3, 2025
48a3943
made the mii image a userControl rather than a templateControl
DirkDoes May 3, 2025
81dbc53
i dont know anymore
DirkDoes May 3, 2025
115d8df
it kinda words now i guess
DirkDoes May 4, 2025
69bb9a8
replaced carousel instances
DirkDoes May 4, 2025
c2fe357
editor faster refresh
DirkDoes May 4, 2025
9788935
better images
DirkDoes May 4, 2025
4d2673b
low quality speed up
DirkDoes May 4, 2025
6f99318
better quality carousel
DirkDoes May 4, 2025
e1b08c0
fix colors for all the icons
DirkDoes May 4, 2025
84926db
make sure it refreshes at all the values
DirkDoes May 4, 2025
87936e2
slider style
DirkDoes May 4, 2025
85ba2af
button styles
DirkDoes May 4, 2025
4bbfe35
buttons now disable propperly
DirkDoes May 4, 2025
8961d90
add beta flag
DirkDoes May 4, 2025
fa4a86e
global icon
DirkDoes May 4, 2025
5190fc5
fix skin color bug
DirkDoes May 4, 2025
5bd00f1
buttons again
DirkDoes May 4, 2025
0c0d0ef
revert test change
DirkDoes May 4, 2025
821ab18
Fixed Database
patchzyy May 5, 2025
423f1fa
Fixed doubled up +
patchzyy May 5, 2025
e8ecb2c
Squashed commit of the following:
patchzyy May 5, 2025
f29e71e
Update layout
patchzyy May 5, 2025
527c481
Forign support + glasses fix + color fix
patchzyy May 5, 2025
58f2bfb
glasses button fix
patchzyy May 5, 2025
df75ffe
Comment out save mii
patchzyy May 5, 2025
9d59e16
Export multiple miis
patchzyy May 5, 2025
dcf676d
Update color
patchzyy May 5, 2025
e418cf8
Fix test
patchzyy May 5, 2025
11f577b
Update WheelWizard.csproj
patchzyy May 5, 2025
e1f6b24
Merge branch 'dev' into MiiEditor
DirkDoes May 5, 2025
a8a56e8
double check if settings are made
patchzyy May 5, 2025
37b688c
fix miidb
patchzyy May 5, 2025
9024043
Brought back license support
patchzyy May 5, 2025
bd3d2a8
Refresh page after updatign miii
patchzyy May 5, 2025
5ef7b99
Update MiiListPage.axaml.cs
patchzyy May 6, 2025
e8c0f80
Reload when importing
patchzyy May 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 63 additions & 58 deletions WheelWizard.Test/Features/MiiDbServiceTests.cs

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions WheelWizard/Features/MiiImages/Domain/IMiiIMagesApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ Task<Stream> GetImageAsync(
int instanceCount = 1,
int cameraXRotate = 0,
int cameraYRotate = 0,
int cameraZRotate = 0,
string lightDirectionMode = "none",
string instanceRotationMode = "model"
int cameraZRotate = 0
);
}
20 changes: 19 additions & 1 deletion WheelWizard/Features/MiiImages/Domain/MiiImageSpecifications.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace WheelWizard.MiiImages.Domain;

public class MiiImageSpecifications
{
// IMPORTANT: if you change this, make sure you also edit the Clone method in the extensions of this feature
public string Name { get; set; } = string.Empty;
public ImageSize Size { get; set; } = ImageSize.small;
public FaceExpression Expression { get; set; } = FaceExpression.normal;
Expand All @@ -23,7 +24,11 @@ public class MiiImageSpecifications
public override string ToString()
{
// If we put all the things in this string, then the Key at least is unique
return $"{Name}_{Size}{Expression}{Type}_{BackgroundColor}{InstanceCount}_{CharacterRotate}{CameraRotate}_{CachePriority}";
var parts = $"{Name}_{Size}{Expression}{Type}";
parts += $"{BackgroundColor}{InstanceCount}";
parts += $"{CharacterRotate}{CameraRotate}";
parts += $"{CachePriority}";
return Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(parts));
}

#region Enums
Expand All @@ -32,12 +37,24 @@ public override string ToString()
public enum FaceExpression
{
normal,
normal_open_mouth,
smile,
smile_open_mouth,
frustrated,
anger,
anger_open_mouth,
blink,
blink_open_mouth,
sorrow,
sorrow_open_mouth,
surprise,
surprise_open_mouth,
wink_right,
wink_left,
like_wink_left,
like_wink_right,
wink_left_open_mouth,
wink_right_open_mouth,
}

public enum ImageSize
Expand All @@ -49,6 +66,7 @@ public enum ImageSize
public enum BodyType
{
face,
face_only,
all_body,
}

Expand Down
32 changes: 32 additions & 0 deletions WheelWizard/Features/MiiImages/Domain/MiiImageVariants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ public static class MiiImageVariants
CachePriority = CacheItemPriority.High,
};

public static readonly MiiImageSpecifications MiiBlockProfile = new()
{
Name = "MiiBlockProfile",
Expression = MiiImageSpecifications.FaceExpression.normal,
Type = MiiImageSpecifications.BodyType.face,
Size = MiiImageSpecifications.ImageSize.medium,
};

public static readonly MiiImageSpecifications OnlinePlayerSmall = new()
{
Name = "OnlinePlayerSmall",
Expand All @@ -23,8 +31,29 @@ public static class MiiImageVariants
CachePriority = CacheItemPriority.Low,
};

public static readonly MiiImageSpecifications MiiEditorSmall = new()
{
Name = "MiiEditorPreviewSmall",
Expression = MiiImageSpecifications.FaceExpression.normal,
Type = MiiImageSpecifications.BodyType.face,
Size = MiiImageSpecifications.ImageSize.medium,
ExpirationSeconds = TimeSpan.FromSeconds(30),
CachePriority = CacheItemPriority.Low,
};
public static readonly MiiImageSpecifications MiiEditorPreviewCarousel = new()
{
Name = "MiiEditorPreviewCarousel",
Expression = MiiImageSpecifications.FaceExpression.normal,
Type = MiiImageSpecifications.BodyType.all_body,
Size = MiiImageSpecifications.ImageSize.medium,
CachePriority = CacheItemPriority.Low,
ExpirationSeconds = TimeSpan.FromSeconds(30),
InstanceCount = 8,
};

public static readonly MiiImageSpecifications CurrentUserSideProfile = new()
{
Name = "CurrentUserSideProfile",
Expression = MiiImageSpecifications.FaceExpression.normal,
Type = MiiImageSpecifications.BodyType.face,
Size = MiiImageSpecifications.ImageSize.medium,
Expand All @@ -33,6 +62,7 @@ public static class MiiImageVariants
};
public static readonly MiiImageSpecifications FriendsSideProfile = new()
{
Name = "FriendsSideProfile",
Expression = MiiImageSpecifications.FaceExpression.normal,
Type = MiiImageSpecifications.BodyType.face,
Size = MiiImageSpecifications.ImageSize.medium,
Expand All @@ -43,9 +73,11 @@ public static class MiiImageVariants

public static readonly MiiImageSpecifications FullBodyCarousel = new()
{
Name = "FullBodyCarousel",
Expression = MiiImageSpecifications.FaceExpression.normal,
Type = MiiImageSpecifications.BodyType.all_body,
Size = MiiImageSpecifications.ImageSize.medium,
ExpirationSeconds = TimeSpan.FromMinutes(10),
InstanceCount = 8,
};
}
21 changes: 21 additions & 0 deletions WheelWizard/Features/MiiImages/MiiImagesExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Numerics;
using WheelWizard.MiiImages.Domain;
using WheelWizard.Services;

Expand All @@ -13,4 +14,24 @@ public static IServiceCollection AddMiiImages(this IServiceCollection services)

return services;
}

public static MiiImageSpecifications Clone(this MiiImageSpecifications specifications)
{
return new MiiImageSpecifications
{
Name = specifications.Name,
Size = specifications.Size,
Expression = specifications.Expression,
Type = specifications.Type,
BackgroundColor = specifications.BackgroundColor,
InstanceCount = specifications.InstanceCount,
CharacterRotate = new(specifications.CharacterRotate.X, specifications.CharacterRotate.Y, specifications.CharacterRotate.Z),
CameraRotate = new(specifications.CameraRotate.X, specifications.CameraRotate.Y, specifications.CameraRotate.Z),
ExpirationSeconds =
specifications.ExpirationSeconds?.TotalSeconds == null
? null
: TimeSpan.FromSeconds(specifications.ExpirationSeconds.Value.TotalSeconds),
CachePriority = specifications.CachePriority,
};
}
}
51 changes: 39 additions & 12 deletions WheelWizard/Features/MiiImages/MiiImagesSingletonService.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Concurrent;
using Avalonia.Media.Imaging;
using Microsoft.Extensions.Caching.Memory;
using WheelWizard.MiiImages.Domain;
Expand All @@ -13,30 +14,56 @@ public interface IMiiImagesSingletonService

public class MiiImagesSingletonService(IApiCaller<IMiiIMagesApi> apiCaller, IMemoryCache cache) : IMiiImagesSingletonService
{
// Track in-flight requests to prevent duplicate API calls
private readonly ConcurrentDictionary<string, SemaphoreSlim> _inFlightRequests = new();

public async Task<OperationResult<Bitmap>> GetImageAsync(Mii? mii, MiiImageSpecifications specifications)
{
var data = MiiStudioDataSerializer.Serialize(mii);
if (data.IsFailure)
return data.Error;

var miiConfigKey = data.Value + specifications;
var isCached = cache.TryGetValue(miiConfigKey, out Bitmap? cachedValue);
if (isCached)

// Even tho we also check it in the semaphore section, we also check here if it's in the cache, just to be tad faster.
if (cache.TryGetValue(miiConfigKey, out Bitmap? cachedValue))
return cachedValue ?? Fail<Bitmap>("Cached image is null.");

var newImageResult = await apiCaller.CallApiAsync(api => GetBitmapAsync(api, data.Value, specifications));
Bitmap? newImage = null;
if (newImageResult.IsSuccess)
newImage = newImageResult.Value;
var requestSemaphore = _inFlightRequests.GetOrAdd(miiConfigKey, _ => new(1, 1));

using (var entry = cache.CreateEntry(miiConfigKey))
try
{
entry.Value = newImage;
entry.SlidingExpiration = specifications.ExpirationSeconds;
entry.Priority = specifications.CachePriority;
}
// Wait to acquire the semaphore - only the first request will proceed immediately
await requestSemaphore.WaitAsync();

// Double-check the cache after acquiring the semaphore
// Another thread might have completed the request while we were waiting
if (cache.TryGetValue(miiConfigKey, out Bitmap? doubleCheckCached))
return doubleCheckCached ?? Fail<Bitmap>("Cached image is null.");

// If we get here, we're the first request and need to call the API
var newImageResult = await apiCaller.CallApiAsync(api => GetBitmapAsync(api, data.Value, specifications));

return newImage ?? Fail<Bitmap>("Failed to get new image.");
Bitmap? newImage = null;
if (newImageResult.IsSuccess)
newImage = newImageResult.Value;

using (var entry = cache.CreateEntry(miiConfigKey))
{
entry.Value = newImage;
entry.SlidingExpiration = specifications.ExpirationSeconds;
entry.Priority = specifications.CachePriority;
}

return newImage ?? Fail<Bitmap>("Failed to get new image.");
}
finally
{
// We can also do it all without try catch. But we need to make sure that whatever happens, we release the semaphore
// So just to be safe, if anything happens, we release the semaphore anyway.
requestSemaphore.Release();
_inFlightRequests.TryRemove(miiConfigKey, out _);
}
}

private static async Task<Bitmap> GetBitmapAsync(IMiiIMagesApi api, string data, MiiImageSpecifications specifications)
Expand Down
3 changes: 2 additions & 1 deletion WheelWizard/Features/MiiImages/MiiStudioDataSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public static OperationResult<string> Serialize(Mii? mii)
visualMiiClone.MiiMole = mii.MiiMole;
visualMiiClone.MiiFavoriteColor = mii.MiiFavoriteColor;
visualMiiClone.MiiFacial = mii.MiiFacial;
visualMiiClone.MiiId = 1; // Mii ID cant be 0 if you want to serialize it so...
// If id is 0, we keep it 0, any other ID will be set to 1.
visualMiiClone.MiiId = (uint)(mii.MiiId == 0 ? 0 : 1);

var serialized = MiiSerializer.Serialize(visualMiiClone);
if (serialized.IsFailure)
Expand Down
48 changes: 38 additions & 10 deletions WheelWizard/Features/WiiManagement/Domain/Mii/Mii.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,53 @@ public class Mii
public MiiScale Height { get; set; } = new(1);
public MiiScale Weight { get; set; } = new(1);

//Mii ID is also referred as Avatar ID
public uint MiiId { get; set; }
public bool IsForeign => (MiiId1 >> 5) == 0b110; // checks if the top 3 bits are set to 110, if so it got blue pants

//Mii ID is also refered as Avatar ID
public byte MiiId1 { get; set; }
public byte MiiId2 { get; set; }
public byte MiiId3 { get; set; }
public byte MiiId4 { get; set; }

public uint MiiId
{
get => (uint)(MiiId1 << 24 | MiiId2 << 16 | MiiId3 << 8 | MiiId4);
set
{
MiiId1 = (byte)(value >> 24);
MiiId2 = (byte)(value >> 16);
MiiId3 = (byte)(value >> 8);
MiiId4 = (byte)(value);
}
}

//This is also referred as Client ID
public byte SystemId0 { get; set; }
public byte SystemId1 { get; set; }
public byte SystemId2 { get; set; }
public byte SystemId3 { get; set; }

public uint SystemId
{
get => (uint)(SystemId0 << 24 | SystemId1 << 16 | SystemId2 << 8 | SystemId3);
set
{
SystemId0 = (byte)(value >> 24);
SystemId1 = (byte)(value >> 16);
SystemId2 = (byte)(value >> 8);
SystemId3 = (byte)(value);
}
}

public MiiFacialFeatures MiiFacial { get; set; } = new(MiiFaceShape.Bread, MiiSkinColor.Light, MiiFacialFeature.None, false, false);

public MiiHair MiiHair { get; set; } = new(0, HairColor.Black, false);
public MiiEyebrow MiiEyebrows { get; set; } = new(0, 0, EyebrowColor.Black, 1, 1, 1);
public MiiEye MiiEyes { get; set; } = new(0, 0, 0, EyeColor.Black, 0, 0);
public MiiNose MiiNose { get; set; } = new(NoseType.Default, 0, 0);
public MiiLip MiiLips { get; set; } = new(0, LipColor.Skin, 0, 0);
public MiiGlasses MiiGlasses { get; set; } = new(GlassesType.None, GlassesColor.Dark, 0, 0);
public MiiFacialHair MiiFacialHair { get; set; } = new(MustacheType.None, BeardType.None, MustacheColor.Black, 0, 0);
public MiiHair MiiHair { get; set; } = new(1, HairColor.Black, false);
public MiiEyebrow MiiEyebrows { get; set; } = new(1, 0, EyebrowColor.Black, 4, 10, 1);
public MiiEye MiiEyes { get; set; } = new(1, 6, 7, EyeColor.Black, 3, 6);
public MiiNose MiiNose { get; set; } = new(NoseType.Default, 6, 4);
public MiiLip MiiLips { get; set; } = new(1, LipColor.Skin, 4, 9);
public MiiGlasses MiiGlasses { get; set; } = new(GlassesType.None, GlassesColor.Dark, 4, 1);
public MiiFacialHair MiiFacialHair { get; set; } = new(MustacheType.None, BeardType.None, MustacheColor.Black, 1, 1);
public MiiMole MiiMole { get; set; } = new(false, 0, 0, 0);

public MiiName CreatorName { get; set; } = new("no name");
}
26 changes: 13 additions & 13 deletions WheelWizard/Features/WiiManagement/Domain/Mii/MiiEnums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public enum MiiFavoriteColor : uint
Red,
Orange,
Yellow,
LightGreen,
Green,
Blue,
LightBlue,
Expand All @@ -13,16 +14,15 @@ public enum MiiFavoriteColor : uint
Brown,
White,
Black,
Gray,
}

public enum MiiFaceShape
{
RoundPointChin,
Teardrop,
Circle,
Oval,
BlobFatChin,
RightAnglePointChin,
Glob,
Pointy,
Bread,
Octagon,
Square,
Expand All @@ -31,8 +31,8 @@ public enum MiiFaceShape
public enum MiiSkinColor
{
Light,
LightTan,
Tan,
Yellow,
Red,
Pink,
DarkBrown,
Brown,
Expand All @@ -51,7 +51,7 @@ public enum MiiFacialFeature
EyeShadow,
Beard,
MouthCorners,
Old,
Wrinkles,
}

public enum HairColor
Expand Down Expand Up @@ -82,7 +82,7 @@ public enum EyeColor : uint
{
Black,
Grey,
Red,
Brown,
Gold,
Blue,
Green,
Expand All @@ -95,7 +95,7 @@ public enum NoseType
Dots,
VShape,
FullNose,
Triangle,
UShape,
FlatC,
UpsideDownC,
Squidward,
Expand Down Expand Up @@ -142,16 +142,16 @@ public enum MustacheColor
LightRed,
Grey,
LightBrown,
Blonde,
Tan,
White,
}

public enum MustacheType
{
None,
Fat,
Thin,
Goatee,
Normal,
Lines,
Droopy,
}

public enum BeardType
Expand Down
Loading
Loading