Add 'Cat Bubbles', fix crash in OML

This commit is contained in:
chirp 2023-03-20 00:48:56 +00:00
parent fe6638d172
commit 6403c8af68
3 changed files with 175 additions and 15 deletions

View file

@ -81,6 +81,7 @@ internal class FoolsManager : IDisposable, IServiceType
new("DailyLifeDuty", "DailyLifeDutyPlugin", "Easily Track Daily and Weekly tasks... in real life", "MidoriKami", typeof(DailyLifeDutyPlugin)),
new("Oops, Maybe Lalafells!", "OopsMaybeLalafellsPlugin", "Turn everyone into Lalafells? Maybe. We haven't quite tested it yet.", "Chrip", typeof(OopsMaybeLalafells)),
new("Screensaver", "ScreensaverPlugin", "Prevent burn-in on loading screens.", "NotNite", typeof(ScreensaverPlugin)),
new("Cat Bubbles", "CatBubblesPlugin", "Enables in-game sdfgasdfgkljewriogdfkjghahfvcxbnmlqpwoeiruty", "Chirp's Cat, Sir Fluffington III", typeof(CatBubblesPlugin))
};
}

View file

@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Timers;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Hooking;
using FFXIVClientStructs.FFXIV.Client.Game;
namespace Dalamud.Fools.Plugins;
public class CatBubblesPlugin : IFoolsPlugin
{
// Plugin
private ClientState ClientState;
public CatBubblesPlugin()
{
ClientState = Service<ClientState>.Get();
var sigscanner = Service<SigScanner>.Get();
var openAddr = sigscanner.ScanText("E8 ?? ?? ?? ?? C7 43 ?? ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ??");
BalloonOpen = Marshal.GetDelegateForFunctionPointer<BalloonOpenDelegate>(openAddr);
var updateAddr = sigscanner.ScanText("48 85 D2 0F 84 ?? ?? ?? ?? 48 89 5C 24 ?? 57 48 83 EC 20 8B 41 0C");
BalloonUpdateHook = Hook<BalloonUpdateDelegate>.FromAddress(updateAddr, BalloonUpdateDetour);
BalloonUpdateHook.Enable();
Timer.Elapsed += OnTimerElapsed;
Timer.Interval = Rng.Next(3, 8) * 1000;
Timer.Start();
}
public void Dispose()
{
Timer.Elapsed -= OnTimerElapsed;
Timer.Stop();
BalloonUpdateHook.Disable();
BalloonUpdateHook.Dispose();
}
private void OnTimerElapsed(object sender, object e)
{
EngageCatMode = true;
Timer.Interval = Rng.Next(35, 150) * 1000;
}
// meow :3
private bool EngageCatMode = false;
private readonly Timer Timer = new();
private readonly Random Rng = new();
private readonly List<string> strs1 = new() { "mrrp", "nya", "mew", "meow", "mraow", "purr" };
private readonly List<string> strs2 = new() { ":3", ":3c", "=^-^=" };
private readonly List<string> strs3 = new() { "zxcvbnm,./`-=", "qweasdzxc", "fghjkl;mnbvcxz", "plokmijnuhkjgs" };
private string GetRandStr(List<string> list)
{
var x = Rng.Next(list.Count);
return list[x];
}
private string GenerateCatSpeak()
{
var items = new List<string>();
var itemCt = Rng.Next(1, 10) + 1;
int lastGen = -1;
bool hasEmoted = false;
for (var i = 0; i < itemCt; i++)
{
var isLast = i == itemCt - 1;
var r = i == 0 ? 0 : Rng.Next(0, 3);
switch (r)
{
case 0:
items.Add(GetRandStr(strs1));
break;
case 1:
if (hasEmoted && !isLast) goto case default;
var item = GetRandStr(strs2);
if (lastGen == 0) item = ' ' + item;
if (!isLast) item += ' ';
items.Add(item);
hasEmoted = true;
break;
case 2:
if (isLast && lastGen != 1) goto case 1;
if (lastGen != 0) goto case default;
items.Add(" ");
break;
default:
items.Add(GetRandStr(strs3));
break;
}
lastGen = r;
}
return string.Join("", items);
}
private delegate nint BalloonOpenDelegate(nint a1, nint a2, string a3, bool a4);
private BalloonOpenDelegate BalloonOpen;
private delegate nint BalloonUpdateDelegate(nint a1, nint a2, nint a3, nint a4);
private Hook<BalloonUpdateDelegate> BalloonUpdateHook = null!;
private unsafe nint BalloonUpdateDetour(nint a1, nint a2, nint a3, nint a4)
{
var balloon = (Balloon*)a1;
if (EngageCatMode && a2 == ClientState.LocalPlayer?.Address && balloon->State == BalloonState.Inactive)
{
var text = GenerateCatSpeak();
balloon->Text.SetString(text);
balloon->State = BalloonState.Active;
balloon->Type = BalloonType.Timer;
balloon->PlayTimer = 5f;
BalloonOpen(a1, a2, text, balloon->UnkBool == 1);
EngageCatMode = false;
}
return BalloonUpdateHook.Original(a1, a2, a3, a4);
}
}

View file

@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Dalamud.Game;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Hooking;
using Dalamud.Logging;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
@ -27,6 +29,7 @@ public class OopsMaybeLalafells : IFoolsPlugin
{
SetupCharacterHook.Disable();
SetupCharacterHook.Dispose();
RedrawAll();
}
@ -36,7 +39,7 @@ public class OopsMaybeLalafells : IFoolsPlugin
var objects = Service<ObjectTable>.Get();
foreach (var obj in objects)
{
if (obj.ObjectIndex > 241) break;
if (obj.ObjectIndex > 241 && obj.ObjectIndex < 301) continue;
var csObject = (GameObject*)obj.Address;
if (csObject == null) continue;
@ -54,30 +57,51 @@ public class OopsMaybeLalafells : IFoolsPlugin
private readonly List<ushort> ReplaceIDs = new() { 84, 85, 86, 87, 88, 89, 90, 91, 257, 258, 581, 597, 744 };
private const string SetupCharacterSig = "E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B D7 E8 ?? ?? ?? ?? 48 8B CF E8 ?? ?? ?? ?? 48 8B C7";
private delegate char SetupCharacterDelegate(nint a1, nint a2);
private Hook<SetupCharacterDelegate> SetupCharacterHook = null!;
private unsafe char SetupCharacterDetour(nint a1, nint a2)
private char SetupCharacterDetour(nint a1, nint a2)
{
// Roll the dice
if (Rng.Next(0, 4) == 0)
try
{
var customize = (byte*)a2;
customize[(int)CustomizeIndex.Race] = 3;
var face = customize + (int)CustomizeIndex.FaceType;
*face = (byte)(1 + ((*face - 1) % 4));
var custom = Marshal.PtrToStructure<CustomizeData>(a2);
var equipTar = (ushort)(customize[(int)CustomizeIndex.Gender] == 0 ? 92 : 93);
for (var i = 1; i < 5; i++)
// Roll the dice
if (custom.Race != 3 && Rng.Next(0, 4) == 0)
{
var equip = (ushort*)(a2 + 28 + (i * 4));
if (ReplaceIDs.Contains(*equip))
*equip = equipTar;
custom.Race = 3;
custom.Tribe = (byte)(((custom.Race * 2) - 1) + 1 - (custom.Tribe % 2));
custom.FaceType = (byte)(1 + ((custom.FaceType - 1) % 4));
custom.ModelType %= 2;
Marshal.StructureToPtr(custom, a2, true);
var equipTar = (ushort)(custom.Gender == 0 ? 92 : 93);
for (var i = 1; i < 5; i++)
{
var ofs = a2 + 28 + (i * 4);
var equip = (ushort)Marshal.ReadInt16(ofs);
if (ReplaceIDs.Contains(equip))
Marshal.WriteInt16(ofs, (short)equipTar);
}
}
}
catch (Exception e)
{
PluginLog.Error(e.ToString(), e);
}
return SetupCharacterHook.Original(a1, a2);
}
// Customize shit
[StructLayout(LayoutKind.Explicit)]
private struct CustomizeData
{
[FieldOffset((int)CustomizeIndex.FaceType)] public byte FaceType;
[FieldOffset((int)CustomizeIndex.ModelType)] public byte ModelType;
[FieldOffset((int)CustomizeIndex.Race)] public byte Race;
[FieldOffset((int)CustomizeIndex.Tribe)] public byte Tribe;
[FieldOffset((int)CustomizeIndex.Gender)] public byte Gender;
}
}