Add hints to codes.

This commit is contained in:
Ottermandias 2024-05-31 16:56:32 +02:00
parent 9c49b66d71
commit edd55087db
4 changed files with 228 additions and 101 deletions

View file

@ -0,0 +1,194 @@
using Dalamud.Interface;
using Glamourer.Services;
using Glamourer.State;
using ImGuiNET;
using OtterGui.Filesystem;
using OtterGui.Raii;
using OtterGui.Services;
using OtterGui.Text;
using OtterGui.Text.EndObjects;
namespace Glamourer.Gui.Tabs.SettingsTab;
public class CodeDrawer(Configuration config, CodeService codeService, FunModule funModule) : IUiService
{
private static ReadOnlySpan<byte> Tooltip
=> "Cheat Codes are not actually for cheating in the game, but for 'cheating' in Glamourer. "u8
+ "They allow for some fun easter-egg modes that usually manipulate the appearance of all players you see (including yourself) in some way."u8;
private static ReadOnlySpan<byte> DragDropLabel
=> "##CheatDrag"u8;
private bool _showCodeHints;
private string _currentCode = string.Empty;
private int _dragCodeIdx = -1;
public void Draw()
{
var show = ImGui.CollapsingHeader("Cheat Codes");
DrawTooltip();
if (!show)
return;
DrawCodeInput();
DrawCopyButtons();
var knownFlags = DrawCodes();
DrawCodeHints(knownFlags);
}
private void DrawCodeInput()
{
var color = codeService.CheckCode(_currentCode).Item2 is not 0 ? ColorId.ActorAvailable : ColorId.ActorUnavailable;
using var border = ImRaii.PushFrameBorder(ImUtf8.GlobalScale, color.Value(), _currentCode.Length > 0);
ImGui.SetNextItemWidth(500 * ImUtf8.GlobalScale + ImUtf8.ItemSpacing.X);
if (ImUtf8.InputText("##Code"u8, ref _currentCode, "Enter Cheat Code..."u8, ImGuiInputTextFlags.EnterReturnsTrue))
{
codeService.AddCode(_currentCode);
_currentCode = string.Empty;
}
ImGui.SameLine();
ImUtf8.Icon(FontAwesomeIcon.ExclamationCircle, ImGui.GetColorU32(ImGuiCol.TextDisabled));
DrawTooltip();
}
private void DrawCopyButtons()
{
var buttonSize = new Vector2(250 * ImUtf8.GlobalScale, 0);
if (ImUtf8.Button("Who am I?!?"u8, buttonSize))
funModule.WhoAmI();
ImUtf8.HoverTooltip(
"Copy your characters actual current appearance including cheat codes or holiday events to the clipboard as a design."u8);
ImGui.SameLine();
if (ImUtf8.Button("Who is that!?!"u8, buttonSize))
funModule.WhoIsThat();
ImUtf8.HoverTooltip(
"Copy your targets actual current appearance including cheat codes or holiday events to the clipboard as a design."u8);
}
private CodeService.CodeFlag DrawCodes()
{
var canDelete = config.DeleteDesignModifier.IsActive();
CodeService.CodeFlag knownFlags = 0;
for (var i = 0; i < config.Codes.Count; ++i)
{
using var id = ImUtf8.PushId(i);
var (code, state) = config.Codes[i];
var (action, flag) = codeService.CheckCode(code);
if (flag is 0)
continue;
var data = CodeService.GetData(flag);
if (ImUtf8.IconButton(FontAwesomeIcon.Trash,
$"Delete this cheat code.{(canDelete ? string.Empty : $"\nHold {config.DeleteDesignModifier} while clicking to delete.")}",
!canDelete))
{
action!(false);
config.Codes.RemoveAt(i--);
codeService.SaveState();
}
knownFlags |= flag;
ImUtf8.SameLineInner();
if (ImUtf8.Checkbox("\0"u8, ref state))
{
action!(state);
codeService.SaveState();
}
var hovered = ImGui.IsItemHovered();
ImGui.SameLine();
ImUtf8.Selectable(code, false);
hovered |= ImGui.IsItemHovered();
DrawSource(i, code);
DrawTarget(i);
if (hovered)
{
using var tt = ImUtf8.Tooltip();
ImUtf8.Text(data.Effect);
}
}
return knownFlags;
}
private void DrawSource(int idx, string code)
{
using var source = ImUtf8.DragDropSource();
if (!source)
return;
if (!DragDropSource.SetPayload(DragDropLabel))
_dragCodeIdx = idx;
ImUtf8.Text($"Dragging {code}...");
}
private void DrawTarget(int idx)
{
using var target = ImUtf8.DragDropTarget();
if (!target.IsDropping(DragDropLabel) || _dragCodeIdx == -1)
return;
if (config.Codes.Move(_dragCodeIdx, idx))
codeService.SaveState();
_dragCodeIdx = -1;
}
private void DrawCodeHints(CodeService.CodeFlag knownFlags)
{
if (knownFlags.HasFlag(CodeService.AllHintCodes))
return;
if (ImUtf8.Button(_showCodeHints ? "Hide Hints"u8 : "Show Hints"u8))
_showCodeHints = !_showCodeHints;
if (!_showCodeHints)
return;
foreach (var code in Enum.GetValues<CodeService.CodeFlag>())
{
if (knownFlags.HasFlag(code))
continue;
var data = CodeService.GetData(code);
if (!data.Display)
continue;
ImGui.Dummy(Vector2.Zero);
ImGui.Separator();
ImGui.Dummy(Vector2.Zero);
ImUtf8.Text(data.Effect);
using var indent = ImRaii.PushIndent(2);
using (ImUtf8.Group())
{
ImUtf8.Text("Capitalized letters: "u8);
ImUtf8.Text("Punctuation: "u8);
}
ImUtf8.SameLineInner();
using (ImUtf8.Group())
{
ImUtf8.Text($"{data.CapitalCount}");
ImUtf8.Text($"{data.Punctuation}");
}
ImUtf8.TextWrapped(data.Hint);
}
}
private static void DrawTooltip()
{
if (!ImGui.IsItemHovered())
return;
ImGui.SetNextWindowSize(new Vector2(400, 0));
using var tt = ImUtf8.Tooltip();
ImUtf8.TextWrapped(Tooltip);
}
}

View file

@ -7,8 +7,6 @@ using Glamourer.Designs;
using Glamourer.Gui.Tabs.DesignTab;
using Glamourer.Interop;
using Glamourer.Interop.PalettePlus;
using Glamourer.Services;
using Glamourer.State;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
@ -19,16 +17,15 @@ namespace Glamourer.Gui.Tabs.SettingsTab;
public class SettingsTab(
Configuration config,
DesignFileSystemSelector selector,
CodeService codeService,
ContextMenuService contextMenuService,
UiBuilder uiBuilder,
GlamourerChangelog changelog,
FunModule funModule,
IKeyState keys,
DesignColorUi designColorUi,
PaletteImport paletteImport,
PalettePlusChecker paletteChecker,
CollectionOverrideDrawer overrides)
CollectionOverrideDrawer overrides,
CodeDrawer codeDrawer)
: ITab
{
private readonly VirtualKey[] _validKeys = keys.GetValidVirtualKeys().Prepend(VirtualKey.NO_KEY).ToArray();
@ -36,8 +33,6 @@ public class SettingsTab(
public ReadOnlySpan<byte> Label
=> "Settings"u8;
private string _currentCode = string.Empty;
public void DrawContent()
{
using var child = ImRaii.Child("MainWindowChild");
@ -57,7 +52,7 @@ public class SettingsTab(
DrawInterfaceSettings();
DrawColorSettings();
overrides.Draw();
DrawCodes();
codeDrawer.Draw();
}
MainWindow.DrawSupportButtons(changelog.Changelog);
@ -297,69 +292,6 @@ public class SettingsTab(
ImGui.NewLine();
}
private void DrawCodes()
{
const string tooltip =
"Cheat Codes are not actually for cheating in the game, but for 'cheating' in Glamourer. They allow for some fun easter-egg modes that usually manipulate the appearance of all players you see (including yourself) in some way.\n\n"
+ "Cheat Codes are generally pop culture references, but it is unlikely you will be able to guess any of them based on nothing. Some codes have been published on the discord server, but other than that, we are still undecided on how and when to publish them or add any new ones. Maybe some will be hidden in the change logs or on the help pages. Or maybe I will just add hints in this section later on.\n\n"
+ "In any case, you are not losing out on anything important if you never look at this section and there is no real reason to go on a treasure hunt for them. It is mostly something I added because it was fun for me.";
var show = ImGui.CollapsingHeader("Cheat Codes");
if (ImGui.IsItemHovered())
{
ImGui.SetNextWindowSize(new Vector2(400, 0));
using var tt = ImRaii.Tooltip();
ImGuiUtil.TextWrapped(tooltip);
}
if (!show)
return;
using (var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale, _currentCode.Length > 0))
{
var color = codeService.CheckCode(_currentCode) != null ? ColorId.ActorAvailable : ColorId.ActorUnavailable;
using var c = ImRaii.PushColor(ImGuiCol.Border, color.Value(), _currentCode.Length > 0);
if (ImGui.InputTextWithHint("##Code", "Enter Cheat Code...", ref _currentCode, 512, ImGuiInputTextFlags.EnterReturnsTrue))
if (codeService.AddCode(_currentCode))
_currentCode = string.Empty;
}
ImGui.SameLine();
ImGuiComponents.HelpMarker(tooltip);
DrawCodeHints();
if (config.Codes.Count <= 0)
return;
for (var i = 0; i < config.Codes.Count; ++i)
{
var (code, state) = config.Codes[i];
var action = codeService.CheckCode(code);
if (action == null)
continue;
if (ImGui.Checkbox(code, ref state))
{
action(state);
codeService.SaveState();
}
}
if (ImGui.Button("Who am I?!?"))
funModule.WhoAmI();
ImGui.SameLine();
if (ImGui.Button("Who is that!?!"))
funModule.WhoIsThat();
}
private void DrawCodeHints()
{
// TODO
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private void Checkbox(string label, string tooltip, bool current, Action<bool> setter)
{

View file

@ -32,6 +32,9 @@ public class CodeService
Dolphins = 0x080000,
}
public static readonly CodeFlag AllHintCodes =
Enum.GetValues<CodeFlag>().Where(f => GetData(f).Display).Aggregate((CodeFlag)0, (f1, f2) => f1 | f2);
public const CodeFlag DyeCodes = CodeFlag.Clown | CodeFlag.World | CodeFlag.Elephants | CodeFlag.Dolphins;
public const CodeFlag GearCodes = CodeFlag.Emperor | CodeFlag.World | CodeFlag.Elephants | CodeFlag.Dolphins;
@ -81,7 +84,7 @@ public class CodeService
var changes = false;
for (var i = 0; i < _config.Codes.Count; ++i)
{
var enabled = CheckCode(_config.Codes[i].Code);
var enabled = CheckCode(_config.Codes[i].Code).Item1;
if (enabled == null)
{
_config.Codes.RemoveAt(i--);
@ -100,7 +103,7 @@ public class CodeService
public bool AddCode(string name)
{
if (CheckCode(name) == null || _config.Codes.Any(p => p.Code == name))
if (CheckCode(name).Item1 is null || _config.Codes.Any(p => p.Code == name))
return false;
_config.Codes.Add((name, false));
@ -108,16 +111,14 @@ public class CodeService
return true;
}
public Action<bool>? CheckCode(string name)
public (Action<bool>?, CodeFlag) CheckCode(string name)
{
var flag = GetCode(name);
if (flag == 0)
return null;
return (null, 0);
var badFlags = ~GetMutuallyExclusive(flag);
return v => _enabled = v ? (_enabled | flag) & badFlags : _enabled & ~flag;
;
return (v => _enabled = v ? (_enabled | flag) & badFlags : _enabled & ~flag, flag);
}
public CodeFlag GetCode(string name)
@ -206,29 +207,29 @@ public class CodeService
_ => [],
};
private static (int CapitalCount, string Punctuation, string Hint, string Effect) GetData(CodeFlag flag)
public static (bool Display, int CapitalCount, string Punctuation, string Hint, string Effect) GetData(CodeFlag flag)
=> flag switch
{
CodeFlag.Clown => (2, ",.", "", "Randomizes dyes for every player."),
CodeFlag.Emperor => (1, ".", "", "Randomizes clothing for every player."),
CodeFlag.Individual => (2, "'!'!", "", "Randomizes customizations for every player."),
CodeFlag.Dwarf => (1, "!", "", "Sets the player character to minimum height and all other players to maximum height."),
CodeFlag.Giant => (2, "!", "", "Sets the player character to maximum height and all other players to minimum height."),
CodeFlag.OopsHyur => (1, "','.", "", "Turns all players to Hyur."),
CodeFlag.OopsElezen => (1, ".", "", "Turns all players to Elezen."),
CodeFlag.OopsLalafell => (2, ",!", "", "Turns all players to Lalafell."),
CodeFlag.OopsMiqote => (3, ".", "", "Turns all players to Miqo'te."),
CodeFlag.OopsRoegadyn => (1, "!", "", "Turns all players to Roegadyn."),
CodeFlag.OopsAuRa => (1, "',.", "", "Turns all players to Au Ra."),
CodeFlag.OopsHrothgar => (1, "',...", "", "Turns all players to Hrothgar."),
CodeFlag.OopsViera => (2, "!'!", "", "Turns all players to Viera."),
CodeFlag.Artisan => (3, ",,!", "", "Enable a debugging mode for the UI. Not really useful."),
CodeFlag.SixtyThree => (2, "", "", "Inverts the gender of every player."),
CodeFlag.Shirts => (1, "-.", "", "Highlights all items in the Unlocks tab as if they were unlocked."),
CodeFlag.World => (1, ",.", "", "Sets every player except the player character themselves to job-appropriate gear."),
CodeFlag.Elephants => (1, "!", "", "Sets every player to the elephant costume in varying shades of pink."),
CodeFlag.Crown => (1, ".", "", "Sets every player with a mentor symbol enabled to the clown's hat."),
CodeFlag.Dolphins => (5, ",", "", "Sets every player to a Namazu hat with different costume bodies."),
_ => (0, string.Empty, string.Empty, string.Empty),
CodeFlag.Clown => (true, 3, ",.", "A punchline uttered by Rorschach.", "Randomizes dyes for every player."),
CodeFlag.Emperor => (true, 1, ".", "A truth that only a child can see.", "Randomizes clothing for every player."),
CodeFlag.Individual => (true, 2, "'!'!", "Something an unwilling prophet tries to convince his followers of.", "Randomizes customizations for every player."),
CodeFlag.Dwarf => (true, 1, "!", "A centuries old metaphor about humility and the progress of science.", "Sets the player character to minimum height and all other players to maximum height."),
CodeFlag.Giant => (true, 2, "!", "A Swift renaming of one of the most famous literary openings of all time.", "Sets the player character to maximum height and all other players to minimum height."),
CodeFlag.OopsHyur => (true, 1, "','.", "An alkaline quote attributed to Marilyn Monroe.", "Turns all players to Hyur."),
CodeFlag.OopsElezen => (true, 1, ".", "A line from a Futurama song about the far future.", "Turns all players to Elezen."),
CodeFlag.OopsLalafell => (true, 2, ",!", "The name of a discontinued plugin.", "Turns all players to Lalafell."),
CodeFlag.OopsMiqote => (true, 3, ".", "A Sandman story.", "Turns all players to Miqo'te."),
CodeFlag.OopsRoegadyn => (true, 2, "!", "A line from a Steven Universe song about his desires.", "Turns all players to Roegadyn."),
CodeFlag.OopsAuRa => (true, 1, "',.", "Something a plumber hates to hear, made to something a scaly hates to hear and initial Au Ra designs.", "Turns all players to Au Ra."),
CodeFlag.OopsHrothgar => (true, 1, "',...", "A meme about the attractiveness of anthropomorphic animals.", "Turns all players to Hrothgar."),
CodeFlag.OopsViera => (true, 2, "!'!", "A panicked exclamation about bunny arithmetics.", "Turns all players to Viera."),
CodeFlag.SixtyThree => (true, 2, "", "The title of a famous LGBTQ-related french play and movie.", "Inverts the gender of every player."),
CodeFlag.Shirts => (true, 2, "-.", "A pre-internet meme about disappointing rewards for an adventure, adapted to this specific cheat code.", "Highlights all items in the Unlocks tab as if they were unlocked."),
CodeFlag.World => (true, 1, ",.", "A quote about being more important than other people.", "Sets every player except the player character themselves to job-appropriate gear."),
CodeFlag.Elephants => (true, 1, "!", "Appropriate lyrics that can also be found in Glamourer's changelogs.", "Sets every player to the elephant costume in varying shades of pink."),
CodeFlag.Crown => (true, 1, ".", "A famous Shakespearean line.", "Sets every player with a mentor symbol enabled to the clown's hat."),
CodeFlag.Dolphins => (true, 5, ",", "The farewell of the second smartest species on Earth.", "Sets every player to a Namazu hat with different costume bodies."),
CodeFlag.Artisan => (false, 3, ",,!", string.Empty, "Enable a debugging mode for the UI. Not really useful."),
_ => (false, 0, string.Empty, string.Empty, string.Empty),
};
}

@ -1 +1 @@
Subproject commit 1d9365164655a7cb38172e1311e15e19b1def6db
Subproject commit 0b5afffda19d3e16aec9e8682d18c8f11f67f1c6