mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-27 19:09:18 +01:00
Add 'Cat Bubbles', fix crash in OML
This commit is contained in:
parent
fe6638d172
commit
6403c8af68
3 changed files with 175 additions and 15 deletions
|
|
@ -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))
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
135
Dalamud/Fools/Plugins/CatBubblesPlugin.cs
Normal file
135
Dalamud/Fools/Plugins/CatBubblesPlugin.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue