summaryrefslogtreecommitdiff
path: root/Player.cs
blob: 49c6fc83b5d0914a3c0f4e7c060d955083bc43f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
//#define QUARANTINED_FEATURES

using System.Numerics;
using System.Runtime.InteropServices;

using ImGuiNET;
using SharpPluginLoader.Core;
using SharpPluginLoader.Core.MtTypes;
using SharpPluginLoader.Core.Entities;
using SharpPluginLoader.Core.Memory;

namespace NewCamera
{
    using static Plugin;

    public unsafe class PlayerExt
    {
        public static MtObject? sPlayer = null;

        private static Player? lastPlayer = null;

        private static int saveSlotIndex = -1;
        private static NativeFunction<nint, nint> getSaveSlotGuiAddr;
        private static NativeFunction<nint, nint> getPlayerSettingsAddr;

        public static nint getPlayerSettings()
        {
            nint rcx = MemoryUtil.Read<nint>(0x145013950);
            if (saveSlotIndex >= 0)
            {
                // MonsterHunterWorld.exe+ADC442 - imul rdx,rcx,0026CC00
                nint rax = saveSlotIndex * 0x26CC00;
                rax += MemoryUtil.Read<nint>(rcx + 0xA8);
                return rax;
            }
            // MonsterHunterWorld.exe+1B8DBB0 - movsxd  rax,dword ptr [rcx+000000A0]
            return getPlayerSettingsAddr.Invoke(rcx);
        }

        public static Player? getPlayerFromSaveSlot()
        {
            nint guiAddr = getSaveSlotGuiAddr.Invoke(MemoryUtil.Read<nint>(0x1451C42B8));
            if (guiAddr == 0x0)
            {
                saveSlotIndex = -1;
                return null;
            }
            int prevSaveSlotIndex = saveSlotIndex;
            // MonsterHunterWorld.exe+ADC42F - movsxd  rcx,dword ptr [rax+00000334]
            saveSlotIndex = MemoryUtil.Read<int>(guiAddr + 0x334);
            if (saveSlotIndex == -1)
            {
                return null;
            }
            // MonsterHunterWorld.exe+1B42047 - add r8,00000740
            nint offset = (sPlayer!.Instance + 0x40) + (0x740 * saveSlotIndex);
            nint playerAddr = 0x0;
            if (offset != 0x0 && MemoryUtil.Read<nint>(offset) == 0x1433FE588 &&
                ((playerAddr = MemoryUtil.Read<nint>(offset + 0x18)) != 0x0))
            {
                return new Player(playerAddr);
            }
            return null;
        }

        public static Player? getPlayer()
        {
            Player? player = Player.MainPlayer;
            if (player != lastPlayer)
            {
                Plugin.OnPlayerChange();
                if (player != null)
                {
                    saveSlotIndex = -1;
                }
                lastPlayer = player;
            }
            return player;
        }

        public static Player? getLastPlayer()
        {
            return lastPlayer;
        }

        public static Player? getPlayerWithFallback()
        {
            Player? player = getPlayer();
            return player != null ? player : getPlayerFromSaveSlot();
        }

        private static bool hideWeapon = false;
        private static NativeAction<nint, int> hideWeaponFunc;
        private static bool hideKnife = false;

        public static class Attach
        {
            public static bool Enable = false;
            public static bool Toggle = false;
            public static bool SwapSides = false;
            public static float Scale = 0.25f;
            public static bool SetIndex = false;
            public static int Index1 = 0;
            public static int Index2 = 1;
            public static nint Bone1 = 0x0;
            public static nint Bone2 = 0x0;

            public static bool Available()
            {
                return Enable && Bone1 != 0x0 && Bone2 != 0x0;
            }

            public static void DropState()
            {
                Bone1 = 0x0;
                Bone2 = 0x0;
            }

            public static void OnBone(nint jiggleData, int jiggleIndex, bool isBody)
            {
                if (SetIndex)
                {
                    if (jiggleIndex == Index1)
                    {
                        Bone1 = jiggleData;
                    }
                    if (jiggleIndex == Index2)
                    {
                        Bone2 = jiggleData;
                    }
                }
                else if (isBody)
                {
                    // Default to the last two jiggle bones on the body.
                    if (Bone1 == 0x0)
                    {
                        Bone1 = jiggleData;
                        Index1 = jiggleIndex;
                    }
                    else
                    {
                        if (Bone2 != 0x0)
                        {
                            Bone1 = Bone2;
                            Index1 = Index2;
                        }
                        Bone2 = jiggleData;
                        Index2 = jiggleIndex;
                    }
                }
            }

            public static void Apply(Player player, float deltaTime, float PadRx, float PadRy, float PadLx, float PadLy)
            {
                Quaternion rotation = new Quaternion(player.Rotation.X, player.Rotation.Y, player.Rotation.Z, player.Rotation.W);
                Vector3 up = Vector3.Transform(new Vector3(0.0f, 1.0f, 0.0f), rotation);
                Vector3 right = Vector3.Transform(new Vector3(1.0f, 0.0f, 0.0f), rotation);
                ref Vector3 jiggle1 = ref MemoryUtil.GetRef<Vector3>((SwapSides ? Bone2 : Bone1) + 0xD0);
                ref Vector3 jiggle2 = ref MemoryUtil.GetRef<Vector3>((SwapSides ? Bone1 : Bone2) + 0xD0);
                float scale = (Int16.MaxValue * deltaTime / Scale);
                jiggle1 -= (right * (PadRx / scale)) + (up * (PadRy / scale));
                jiggle2 -= (right * (PadLx / scale)) + (up * (PadLy / scale));
            }

            public static void Draw(float width)
            {
                if (ImGui.Checkbox("Attach to Chest", ref Enable))
                {
                    if (!Enable)
                    {
                        Toggle = false;
                    }
                }
                if (ImGui.BeginItemTooltip())
                {
                    ImGui.Text("While in Free Camera, hold LT + RT and move your joysticks (armor has to have jiggle physics).");
                    ImGui.EndTooltip();
                }
                if (!Enable)
                {
                    ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f);
                }
                ImGui.SameLine();
                ImGui.Checkbox("Toggle", ref Toggle);
                ImGui.SameLine();
                ImGui.Checkbox("Swap Sides", ref SwapSides);
                ImGui.SetNextItemWidth(width * 0.35f);
                ImGui.DragFloat("Strength", ref Scale, 0.001f);
                ImGui.Checkbox("Set Index", ref SetIndex);
                ImGui.SameLine();
                if (!SetIndex)
                {
                    ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f);
                }
                if (SetIndex && Bone1 == 0x0)
                {
                    ImGui.PushStyleColor(ImGuiCol.Text, 0xFF0000FF);
                }
                ImGui.SetNextItemWidth(width * 0.17f);
                if (ImGui.InputInt("##Index 1", ref Index1, 1))
                {
                    if (Index1 < 0) Index1 = 0;
                }
                if (SetIndex && Bone1 == 0x0)
                {
                    ImGui.PopStyleColor();
                }
                ImGui.SameLine();
                if (SetIndex && Bone2 == 0x0)
                {
                    ImGui.PushStyleColor(ImGuiCol.Text, 0xFF0000FF);
                }
                ImGui.SetNextItemWidth(width * 0.17f);
                if (ImGui.InputInt("##Index 2", ref Index2, 1))
                {
                    if (Index2 < 0) Index2 = 0;
                }
                if (SetIndex && Bone2 == 0x0)
                {
                    ImGui.PopStyleColor();
                }
                if (!SetIndex)
                {
                    ImGui.PopStyleVar();
                }
                if (!Enable)
                {
                    ImGui.PopStyleVar();
                }
            }
        }

        private enum ZoneState : int
        {
            Unknown = 0,
            Hub = 1,
            Combat = 2
        }

#if QUARANTINED_FEATURES
        private static ZoneState forceZoneState = ZoneState.Unknown;
        private static ZoneState lastZoneState = ZoneState.Unknown;
        private static NativeAction<nint, int> setZoneState;
        private static NativeAction<nint> setPlayerController1;
        private static NativeAction<nint> setPlayerController5;
        private static bool zoneStateManualInvoke = false;
        private static bool forcePassiveInCombatZone = false;
        private static Patch zoneStateForcePassive1;
        private static Patch zoneStateForcePassive2;
        private static bool forceCombatInPassiveZone = false;
        private static Patch zoneStateForceCombat1;
        private static Patch zoneStateForceCombat2;
#endif
        private delegate void ZoneStateDelegate(nint player, int flags);
        private static Hook<ZoneStateDelegate>? setZoneStateHook;

        private static bool enableCrawl = false;
        private static NativeAction<nint, nint> procEnvironmentCollision;
        private static nint psuedoObject1;
        private static nint psuedoObject2;
        private delegate void CollisionCheckDelegate(nint unknownPtr, nint unknownPtr2);
        private static Hook<CollisionCheckDelegate>? collisionCheckHook;

        private static bool allowHotSpringsAnywhere = false;
        private static Patch jmpOverHotSpringsEval;

        private static bool disableHotSpringsSteam = false;
        private static Patch jmpOverHotSpringsSteam;

        private static float playerOpacityOverride = 1.0f;
        private delegate void RefreshEntityParams(nint entity, nint unknownPtr2);
        private static Hook<RefreshEntityParams>? refreshEntityParamsHook;

        private static bool overridePlayerWetness = false;
        private static Patch noopPlayerWetnessUpdate;
        private static Patch jmpOverChangeEquipmentWetnessUpdate_1;
        private static Patch jmpOverChangeEquipmentWetnessUpdate_2;
        private static float wholeBodyWetness = 0.0f;

        private static void overridePlayerWetnessEnable()
        {
            noopPlayerWetnessUpdate.Enable();
            jmpOverChangeEquipmentWetnessUpdate_1.Enable();
            jmpOverChangeEquipmentWetnessUpdate_2.Enable();
        }

        private static void overridePlayerWetnessDisable()
        {
            noopPlayerWetnessUpdate.Disable();
            jmpOverChangeEquipmentWetnessUpdate_1.Disable();
            jmpOverChangeEquipmentWetnessUpdate_2.Disable();
        }

        private static void writePlayerWholeBodyWetness(nint wetnessAddr)
        {
            MemoryUtil.GetRef<float>(wetnessAddr) = wholeBodyWetness;
            MemoryUtil.GetRef<float>(wetnessAddr + 0x28) = wholeBodyWetness;
            MemoryUtil.GetRef<float>(wetnessAddr + 0x50) = wholeBodyWetness;
            MemoryUtil.GetRef<float>(wetnessAddr + 0x78) = wholeBodyWetness;
        }

        private static bool disableCollision = false;
        private static bool disableExtraCollision = false;
        private static bool disableGravity = false;
        private static bool disableExtraGravity = false;
        private static Patch disableXCollision;
        private static Patch disableYCollision;
        private static Patch disableSecondaryYCollision;
        private static Patch disableZCollision;
        private static Patch disableExtraYCollision;
        private static Patch disableGravityYUpdate;
        private static Patch disableGravityXZUpdate;
        private static Patch disableGravityEval;

        private static void disableCollisionEnable()
        {
            disableXCollision.Enable();
            disableYCollision.Enable();
            disableSecondaryYCollision.Enable();
            disableZCollision.Enable();
        }

        private static void disableCollisionDisable()
        {
            disableXCollision.Disable();
            disableYCollision.Disable();
            disableSecondaryYCollision.Disable();
            disableZCollision.Disable();
        }

        private static void disableExtraCollisionEnable()
        {
            disableExtraYCollision.Enable();
        }

        private static void disableExtraCollisionDisable()
        {
            disableExtraYCollision.Disable();
        }

        private static void disableGravityEnable()
        {
            disableGravityYUpdate.Enable();
        }

        private static void disableGravityDisable()
        {
            disableGravityYUpdate.Disable();
        }

        private static void disableExtraGravityEnable()
        {
            disableGravityEval.Enable();
        }

        private static void disableExtraGravityDisable()
        {
            disableGravityEval.Disable();
        }

        public static void Initialize()
        {
            getSaveSlotGuiAddr = new NativeFunction<nint, nint>(0x141AC0B60);
            getPlayerSettingsAddr = new NativeFunction<nint, nint>(0x141B8DBB0);

            /*
            // MonsterHunterWorld.exe+1789ABA - mov rdi,[rcx+000076B0]
            nint baseAddr = MemoryUtil.Read<nint>(player.Instance + 0x76B0);
            if (baseAddr != 0x0)
            {
                MemoryUtil.WriteBytes(baseAddr + 0x1A98, [0x1]);
                int weaponParts = MemoryUtil.Read<int>(baseAddr + 0x2250);
                for (int i = 0; i < weaponParts; i++)
                {
                    nint partsAddr = MemoryUtil.Read<nint>(baseAddr + ((i + 0x16B) * 24));
                    if (partsAddr != 0x0)
                    {
                        MemoryUtil.WriteBytes(partsAddr + 0x1A98, [0x1]);
                    }
                }
            }
            */
            hideWeaponFunc = new NativeAction<nint, int>(0x141789AB0);

#if QUARANTINED_FEATURES
            // MonsterHunterWorld.exe+20354F7 - call MonsterHunterWorld.exe+1F73850
            setZoneState = new NativeAction<nint, int>(0x142035020);
            setPlayerController1 = new NativeAction<nint>(0x141F73850);
            setPlayerController5 = new NativeAction<nint>(0x14118DDC0);
            zoneStateForcePassive1 = new Patch(0x140256A3F + 0x6, [0x1]);
            zoneStateForcePassive2 = new Patch(0x141AC2865 + 0x6, [0x1]);
            zoneStateForceCombat1 = new Patch(0x141AC2938 + 0x6, [0x0]);
            zoneStateForceCombat2 = new Patch(0x141AC28EC + 0x6, [0x0]);
#endif
            setZoneStateHook = Hook.Create<ZoneStateDelegate>(0x142035020, SetZoneStateHook); // nint, int

            procEnvironmentCollision = new NativeAction<nint, nint>(0x141F737D0);
            psuedoObject1 = (nint)NativeMemory.AllocZeroed(0x7C);
            MemoryUtil.WriteBytes(psuedoObject1 + 0x30, [0x03]);
            psuedoObject2 = (nint)NativeMemory.AllocZeroed(0x7C);
            MemoryUtil.WriteBytes(psuedoObject2 + 0x30, [0x08]);
            collisionCheckHook = Hook.Create<CollisionCheckDelegate>(0x1411C4E50, CollisionCheckHook); // nint, nint

            // Player + ECCC = How far underwater is the player.
            jmpOverHotSpringsEval = new Patch(0x1417C1B03, [0xEB]);
            jmpOverHotSpringsSteam = new Patch(0x14203A494, [0xE9, 0x8D, 0x00, 0x00, 0x00, 0x90]);

            refreshEntityParamsHook = Hook.Create<RefreshEntityParams>(0x141F605F0, RefreshEntityParamsHook); // nint, nint

            noopPlayerWetnessUpdate = new Patch(0x14203E893, [0x90, 0x90, 0x90, 0x90, 0x90]);
            jmpOverChangeEquipmentWetnessUpdate_1 = new Patch(0x14203E310, [0xEB]); // je -> jmp.
            jmpOverChangeEquipmentWetnessUpdate_2 = new Patch(0x14203E397, [0xE9, 0xB9, 0x00, 0x00, 0x00, 0x90]); // je -> jmp.

            // Noop collision checks.
            nint addr = PatternScanner.FindFirst(Pattern.FromString("F3 0F 11 06 F3 0F 10 48 04 F3 0F 58 4E 04 F3 0F 11 4E 04 F3 0F 10 40 08 F3 0F 58 46 08 F3 0F 11 46 08 44 8B AF E0 0B 00 00"));
            Assert(addr == 0x141C001B5);
            disableXCollision = new Patch(addr, [0x90, 0x90, 0x90, 0x90]);
            Assert(addr + 0xE == 0x141C001C3);
            disableYCollision = new Patch(addr + 0xE, [0x90, 0x90, 0x90, 0x90, 0x90]);
            Assert(addr + 0x1D == 0x141C001D2);
            disableZCollision = new Patch(addr + 0x1D, [0x90, 0x90, 0x90, 0x90, 0x90]);

            addr = PatternScanner.FindFirst(Pattern.FromString("F3 0F 11 46 04 F3 0F 10 46 08 F3 0F 5C C2 F3 0F 11 46 08 ?? ?? F3 0F"));
            Assert(addr == 0x141C000D2);
            disableSecondaryYCollision = new Patch(addr, [0x90, 0x90, 0x90, 0x90, 0x90]);

            addr = PatternScanner.FindFirst(Pattern.FromString("F3 0F 11 46 04 F3 0F 10 58 08 F3 0F 58 5E 08 F3 0F 11 5E 08 44 8B 87 D0 0B 00 00 41 F6 C0 03"));
            Assert(addr == 0x141BFFF90);
            disableExtraYCollision = new Patch(addr, [0x90, 0x90, 0x90, 0x90, 0x90]);

            addr = PatternScanner.FindFirst(Pattern.FromString("F3 0F 11 83 80 00 00 00 F3 0F 11 8B 84 00 00 00 F3 0F 11 93 88 00 00 00 89 B3 8C 00 00 00 8B 83 A4 01 00 00 C1 E8 05 44 0F 29 A4 24 D0 01 00 00 44 0F 29 B4 24 B0 01 00 00 A8 01"));
            Assert(addr + 0x8 == 0x1413259ED);
            disableGravityYUpdate = new Patch(addr + 0x8, [
                0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
            ]);
            disableGravityXZUpdate = new Patch(addr, [
                0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
                0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // Will already be applied.
                0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90
            ]);

            addr = PatternScanner.FindFirst(Pattern.FromString("F3 0F 11 97 54 15 00 00 F3 0F 11 8F 58 15 00 00 F3 0F 10 47 68 F3 0F 5E D8 44 89 B7 6C 15 00 00 F3 0F 5E D0 F3 0F 5E C8 F3 0F 11 9F 60 15 00 00 F3 0F 11 97 64 15 00 00 F3 0F 11 8F 68 15 00 00"));
            Assert(addr + 0x40 == 0x141BFFF75);
            disableGravityEval = new Patch(addr + 0x40, [0x90, 0x90, 0x90, 0x90, 0x90]); // call MonsterHunterWorld.exe+1325810.
        }

        public static void OnLoad()
        {
            sPlayer = SingletonManager.GetSingleton("sPlayer")!;
        }

        public static void OnUpdate()
        {
            Player? player = getPlayerWithFallback();
            if (player != null)
            {
                if (hideWeapon)
                {
                    hideWeaponFunc.Invoke(player.Instance, 0);
                }
                if (hideKnife)
                {
                    // Emulate what Carving and Flourish emote do at
                    // MonsterHunterWorld.exe+1F68832 - bts eax,06
                    // if set, skips
                    // MonsterHunterWorld.exe+203A0D2 - bts ecx,01
                    MemoryUtil.GetRef<int>(player.Instance + 0x8938) |= (1 << 6);
                    MemoryUtil.GetRef<int>(player.Instance + 0x8968) |= (1 << 6);
                }
            }
        }

        private static void SetZoneStateHook(nint player, int flags)
        {
            debugLog($"SetZoneStateHook(0x{player:X}, 0x{flags:X}) @ {updateTick}");

#if QUARANTINED_FEATURES
            nint zoneStateAddr = MemoryUtil.Read<nint>(0x1451C42B8);
            ref byte zoneState = ref MemoryUtil.GetRef<byte>(zoneStateAddr + 0xD2EA);
            if (!zoneStateManualInvoke)
            {
                lastZoneState = (zoneState == 0x1) ? ZoneState.Hub : ZoneState.Combat;
            }
            else
            {
                zoneStateManualInvoke = false;
            }

            ZoneState overrideZoneState = (forceZoneState == ZoneState.Unknown) ? lastZoneState : forceZoneState;
            if (overrideZoneState != ZoneState.Unknown)
            {
                zoneState = ByteFlag(overrideZoneState == ZoneState.Hub);
            }
#endif

            float headWet = 0.0f, bodyWet = 0.0f, waistWet = 0.0f, legsWet = 0.0f;
            nint wetnessAddr = player + 0x13BD0;
            if (overridePlayerWetness)
            {
                headWet = MemoryUtil.Read<float>(wetnessAddr);
                bodyWet = MemoryUtil.Read<float>(wetnessAddr + 0x28);
                waistWet = MemoryUtil.Read<float>(wetnessAddr + 0x50);
                legsWet = MemoryUtil.Read<float>(wetnessAddr + 0x78);
            }

            setZoneStateHook!.Original(player, flags);

            if (overridePlayerWetness)
            {
                MemoryUtil.GetRef<float>(wetnessAddr) = headWet;
                MemoryUtil.GetRef<float>(wetnessAddr + 0x28) = bodyWet;
                MemoryUtil.GetRef<float>(wetnessAddr + 0x50) = waistWet;
                MemoryUtil.GetRef<float>(wetnessAddr + 0x78) = legsWet;
            }
        }

        private static void CollisionCheckHook(nint unknownPtr, nint unknownPtr2)
        {
            debugLog($"CollisionCheckHook(0x{unknownPtr:X}, 0x{unknownPtr2:X}) @ {updateTick}");

            Player? player = null;
            if (enableCrawl && (player = getPlayer()) != null)
            {
                nint controlsAddr = MemoryUtil.Read<nint>(player.Instance + 0x12608);
                bool combatControls = controlsAddr != 0x0 && MemoryUtil.Read<byte>(controlsAddr + 0xB18) == 0x80;
                if (combatControls)
                {
                    procEnvironmentCollision.Invoke(player.Instance, psuedoObject1);
                    procEnvironmentCollision.Invoke(player.Instance, psuedoObject2);
                }
                else
                {
                    enableCrawl = false;
                }
            }

            collisionCheckHook!.Original(unknownPtr, unknownPtr2);
        }

        private static void RefreshEntityParamsHook(nint entity, nint unknownPtr2)
        {
            debugLog($"RefreshEntityParamsHook(0x{entity:X}, 0x{unknownPtr2:X}) @ {updateTick}");

            refreshEntityParamsHook!.Original(entity, unknownPtr2);

            Player? player = null;
            if (playerOpacityOverride != 1.0f && (player = getPlayerWithFallback()) != null && entity == player)
            {
                MemoryUtil.GetRef<float>(player.Instance + 0x78E0) = playerOpacityOverride;
            }
        }

        public static void DrawPlayerInfo(Player player, float width)
        {
            nint playerSettings = getPlayerSettings();

            ImGui.Text($"{Marshal.PtrToStringAnsi(playerSettings + 0x50)}");

            ImGui.Checkbox("Hide Weapon", ref hideWeapon);
            ImGui.SameLine();
            ImGui.Checkbox("Hide Knife", ref hideKnife);

            ImGui.SetCursorPos(ImGui.GetCursorPos() + new Vector2(0.0f, 6.0f));
            ImGui.Text("Head Armor Display");
            // MonsterHunterWorld.exe+11A2967 - cmp byte ptr [rax+001403E1],01
            ref byte showHeadArmor = ref MemoryUtil.GetRef<byte>(playerSettings + 0x1403E1);
            int showHeadArmorInt = showHeadArmor;
            ImGui.SameLine();
            ImGui.SetCursorPos(ImGui.GetCursorPos() - new Vector2(0.0f, 6.0f));
            if (ImGui.RadioButton("Show", ref showHeadArmorInt, 0))
            {
                showHeadArmor = (byte)showHeadArmorInt;
            }
            ImGui.SameLine();
            ImGui.SetCursorPos(ImGui.GetCursorPos() - new Vector2(0.0f, 6.0f));
            if (ImGui.RadioButton("Hide", ref showHeadArmorInt, 1))
            {
                showHeadArmor = (byte)showHeadArmorInt;
            }
            ImGui.SameLine();
            ImGui.SetCursorPos(ImGui.GetCursorPos() - new Vector2(0.0f, 6.0f));
            if (ImGui.RadioButton("Hide in Cutscenes", ref showHeadArmorInt, 2))
            {
                showHeadArmor = (byte)showHeadArmorInt;
            }

            ImGui.Separator();

            Poser.DrawUI(width, player);

            nint controlsAddr = MemoryUtil.Read<nint>(player.Instance + 0x12608);
            nint zoneStateAddr = MemoryUtil.Read<nint>(0x1451C42B8);
            ZoneState zoneState = ZoneState.Unknown;
            if (controlsAddr != 0x0 && zoneStateAddr != 0x0)
            {
#if !QUARANTINED_FEATURES
                ImGui.Text($"Current Zone State: {((MemoryUtil.GetRef<byte>(zoneStateAddr + 0xD2EA) == 0x1) ? "Hub" : "Combat")}");
#endif
                ref byte passiveFlag = ref MemoryUtil.GetRef<byte>(player.Instance + 0x7626);
                ref byte passiveFlag2 = ref MemoryUtil.GetRef<byte>(MemoryUtil.Read<nint>(player.Instance + 0x7D20) + 0x9B8);
                bool passive = passiveFlag == 0x1 && passiveFlag2 == 0x1;
                if (ImGui.Checkbox("Passive", ref passive))
                {
                    passiveFlag = ByteFlag(passive);
                    passiveFlag2 = ByteFlag(passive);
                }
                if (ImGui.BeginItemTooltip())
                {
                    ImGui.Text("Passive: Calm camera and your hunter looks neutral/smiles.\nCombat: Intense camera and your hunter looks angry.\nIf you have Passive set and Combat Controls on, or vice versa, expect glitchy behavior.");
                    ImGui.EndTooltip();
                }
                bool combatControls = MemoryUtil.Read<byte>(controlsAddr + 0xB18) == 0x80;
                zoneState = combatControls ? ZoneState.Combat : ZoneState.Hub;
#if QUARANTINED_FEATURES
                if (ImGui.Checkbox("Combat Controls", ref combatControls))
                {
                    passiveFlag = ByteFlag(!combatControls);
                    passiveFlag2 = ByteFlag(!combatControls);
                    setPlayerController1.Invoke(player.Instance);
                    setPlayerController5.Invoke(controlsAddr);
                }
                int zoneStateInt = (int)forceZoneState;
                ImGui.SetCursorPos(ImGui.GetCursorPos() + new Vector2(0.0f, 6.0f));
                ImGui.Text($"Force Zone State (Current: {((MemoryUtil.GetRef<byte>(zoneStateAddr + 0xD2EA) == 0x1) ? "Hub" : "Combat")})");
                if (ImGui.BeginItemTooltip())
                {
                    ImGui.Text("This will apply when moving to a different area.");
                    ImGui.EndTooltip();
                }
                ImGui.SameLine();
                ImGui.SetCursorPos(ImGui.GetCursorPos() - new Vector2(0.0f, 6.0f));
                ImGui.RadioButton("Off", ref zoneStateInt, 0);
                ImGui.SameLine();
                ImGui.SetCursorPos(ImGui.GetCursorPos() - new Vector2(0.0f, 6.0f));
                ImGui.RadioButton("Hub", ref zoneStateInt, 1);
                ImGui.SameLine();
                ImGui.SetCursorPos(ImGui.GetCursorPos() - new Vector2(0.0f, 6.0f));
                ImGui.RadioButton("Combat", ref zoneStateInt, 2);
                forceZoneState = (ZoneState)zoneStateInt;
                if (ImGui.Button("Run Change Zone State"))
                {
                    zoneStateManualInvoke = true;
                    setZoneState.Invoke(player.Instance, 0x000106C0); // or 0x00010780.
                }
                if (ImGui.BeginItemTooltip())
                {
                    ImGui.Text("You can use this to apply the value of Force Zone State.\nIf you're in a map and Force Zone State is set to Off or Combat, this will send you back to camp.");
                    ImGui.EndTooltip();
                }
                if (ImGui.Checkbox("Force Passive in Combat Zones", ref forcePassiveInCombatZone))
                {
                    if (forcePassiveInCombatZone)
                    {
                        zoneStateForcePassive1.Enable();
                        zoneStateForcePassive2.Enable();
                    }
                    else
                    {
                        zoneStateForcePassive1.Disable();
                        zoneStateForcePassive2.Disable();
                    }
                }
                if (ImGui.Checkbox("Force Combat in Passive Zones", ref forceCombatInPassiveZone))
                {
                    if (forceCombatInPassiveZone)
                    {
                        zoneStateForceCombat1.Enable();
                        zoneStateForceCombat2.Enable();
                    }
                    else
                    {
                        zoneStateForceCombat1.Disable();
                        zoneStateForceCombat2.Disable();
                    }
                }
#endif
            }

            if (zoneState != ZoneState.Combat)
            {
                ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true);
                ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f);
            }
            if (ImGui.Checkbox("Force Crawl", ref enableCrawl))
            {
                if (enableCrawl)
                {
                    MemoryUtil.GetRef<float>(psuedoObject2 + 0x70) = player.Position.X + player.Forward.X;
                    MemoryUtil.GetRef<float>(psuedoObject2 + 0x74) = player.Position.Y + player.Forward.Y;
                    MemoryUtil.GetRef<float>(psuedoObject2 + 0x78) = player.Position.Z + player.Forward.Z;

                    Quaternion playerRotation = new Quaternion(player.Forward.X, player.Forward.Y, player.Forward.Z, 0.0f);
                    Quaternion objectRotation = getRight(playerRotation);

                    MemoryUtil.GetRef<float>(psuedoObject2 + 0x40) = objectRotation.X;
                    MemoryUtil.GetRef<float>(psuedoObject2 + 0x44) = objectRotation.Y;
                    MemoryUtil.GetRef<float>(psuedoObject2 + 0x48) = objectRotation.Z;
                    MemoryUtil.GetRef<float>(psuedoObject2 + 0x4C) = objectRotation.W;

                    MemoryUtil.GetRef<float>(psuedoObject2 + 0x50) = 0.0f;
                    MemoryUtil.GetRef<float>(psuedoObject2 + 0x54) = 1.0f;
                    MemoryUtil.GetRef<float>(psuedoObject2 + 0x58) = 0.0f;
                    MemoryUtil.GetRef<float>(psuedoObject2 + 0x5C) = 0.0f;

                    MemoryUtil.GetRef<float>(psuedoObject2 + 0x60) = playerRotation.X;
                    MemoryUtil.GetRef<float>(psuedoObject2 + 0x64) = playerRotation.Y;
                    MemoryUtil.GetRef<float>(psuedoObject2 + 0x68) = playerRotation.Z;
                    MemoryUtil.GetRef<float>(psuedoObject2 + 0x6C) = playerRotation.W;
                }
            }
            if (zoneState != ZoneState.Combat)
            {
                ImGui.PopItemFlag();
                ImGui.PopStyleVar();
                if (ImGui.BeginItemTooltip())
                {
                    ImGui.Text("You have to be out in a map to use this.");
                    ImGui.EndTooltip();
                }
            }
            else
            {
                if (ImGui.BeginItemTooltip())
                {
                    ImGui.Text("Simulate crawling under an object in the direction your character was facing when enabled.");
                    ImGui.EndTooltip();
                }
            }

            if (zoneState != ZoneState.Hub)
            {
                ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true);
                ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f);
            }
            if (ImGui.Button("Sit in Hot Springs"))
            {
                // Run sit in hot springs action (top of function).
                // MonsterHunterWorld.exe+17601F0 - mov [rsp+08],rbx
                MemoryUtil.WriteBytes(player.ActionController.Instance + 0xC0, [0x65, 0x00, 0x00, 0x00]);
                MemoryUtil.WriteBytes(player.ActionController.Instance + 0xBC, [0x01, 0x00, 0x00, 0x00]);
            }
            if (zoneState != ZoneState.Hub)
            {
                ImGui.PopItemFlag();
                ImGui.PopStyleVar();
                if (ImGui.BeginItemTooltip())
                {
                    ImGui.Text("You have to be in a hub area to use this.");
                    ImGui.EndTooltip();
                }
            }

            if (ImGui.Checkbox("Allow Hot Springs Anywhere", ref allowHotSpringsAnywhere))
            {
                if (allowHotSpringsAnywhere)
                {
                    jmpOverHotSpringsEval.Enable();
                }
                else
                {
                    jmpOverHotSpringsEval.Disable();
                }
            }
            if (ImGui.BeginItemTooltip())
            {
                ImGui.Text("Disable check for being about halfway submerged in water to stay sitting.");
                ImGui.EndTooltip();
            }

            if (ImGui.Checkbox("Disable Hot Springs Steam", ref disableHotSpringsSteam))
            {
                if (disableHotSpringsSteam)
                {
                    jmpOverHotSpringsSteam.Enable();
                }
                else
                {
                    jmpOverHotSpringsSteam.Disable();
                }
            }
            if (ImGui.BeginItemTooltip())
            {
                ImGui.Text("It will take a couple of seconds to fade away.");
                ImGui.EndTooltip();
            }

            ImGui.PushID("Opacity");
            ref float playerOpacity = ref MemoryUtil.GetRef<float>(player.Instance + 0x78E0);
            if (ImGui.Button("Reset"))
            {
                playerOpacity = 1.0f;
                playerOpacityOverride = 1.0f;
            }
            ImGui.SameLine();
            ImGui.SetNextItemWidth(width * 0.3f);
            if (ImGui.DragFloat("Opacity", ref playerOpacity, 0.01f, 0.0f, Single.MaxValue))
            {
                playerOpacityOverride = playerOpacity;
            }
            ImGui.PopID();

            ImGui.PushID("Wetness");
            if (ImGui.CollapsingHeader("Wetness"))
            {
                nint wetnessAddr = player.Instance + 0x13BD0;
                ImGui.PushItemWidth(width * 0.15f);
                ImGui.DragFloat("Head", ref MemoryUtil.GetRef<float>(wetnessAddr), 0.005f);
                ImGui.SameLine();
                ImGui.DragFloat("Body", ref MemoryUtil.GetRef<float>(wetnessAddr + 0x28), 0.005f);
                ImGui.DragFloat("Waist", ref MemoryUtil.GetRef<float>(wetnessAddr + 0x50), 0.005f);
                ImGui.SameLine();
                ImGui.DragFloat("Legs", ref MemoryUtil.GetRef<float>(wetnessAddr + 0x78), 0.005f);
                ImGui.PopItemWidth();
                ImGui.SetNextItemWidth(width * 0.35f);
                if (ImGui.DragFloat("Whole Body", ref wholeBodyWetness, 0.005f))
                {
                    if (overridePlayerWetness)
                    {
                        writePlayerWholeBodyWetness(wetnessAddr);
                    }
                }
                if (ImGui.Button("Reset"))
                {
                    wholeBodyWetness = 0.0f;
                    writePlayerWholeBodyWetness(wetnessAddr);
                }
                ImGui.SameLine();
                if (ImGui.Checkbox("Override Player Wetness", ref overridePlayerWetness))
                {
                    if (overridePlayerWetness)
                    {
                        overridePlayerWetnessEnable();
                        writePlayerWholeBodyWetness(wetnessAddr);
                    }
                    else
                    {
                        overridePlayerWetnessDisable();
                    }
                }
                ImGui.Separator();
            }
            ImGui.PopID();

            ImGui.PushID("Position");
            ImGui.PushItemWidth(width * 0.75f);
            ImGui.DragFloat3("Position", ref player.Position, 0.5f);
            Vector3 forward = new Vector3(player.Forward.X, player.Forward.Y, player.Forward.Z);
            ImGui.DragFloat3("Forward", ref forward, 0.0f);
            Vector4 rotation = new Vector4(player.Rotation.X, player.Rotation.Y, player.Rotation.Z, player.Rotation.W);
            ImGui.SliderFloat4("Rotation", ref rotation, -1.0f, 1.0f);
            if (Models.DrawRotate("Rotate", rotation, out Vector4 result))
            {
                player.Rotation = new MtQuaternion(result.X, result.Y, result.Z, result.W);
            }
            ImGui.PopItemWidth();
            if (ImGui.Button("Upright"))
            {
                player.Rotation.X = 0.0f;
                player.Rotation.Z = 0.0f;
            }
            ImGui.PopID();

            if (ImGui.Checkbox("Disable Collision", ref disableCollision))
            {
                if (disableCollision)
                {
                    disableCollisionEnable();
                }
                else
                {
                    if (disableExtraGravity)
                    {
                        disableExtraGravity = false;
                        disableExtraGravityDisable();
                    }
                    if (disableGravity)
                    {
                        disableGravity = false;
                        disableGravityDisable();
                    }
                    if (disableExtraCollision)
                    {
                        disableExtraCollision = false;
                        disableExtraCollisionDisable();
                    }
                    disableCollisionDisable();
                }
            }
            if (!disableCollision)
            {
                ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true);
                ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f);
            }
            if (ImGui.Checkbox("Disable Another Y Collision", ref disableExtraCollision))
            {
                if (disableExtraCollision)
                {
                    disableExtraCollisionEnable();
                }
                else
                {
                    if (disableExtraGravity)
                    {
                        disableExtraGravity = false;
                        disableExtraGravityDisable();
                    }
                    if (disableGravity)
                    {
                        disableGravity = false;
                        disableGravityDisable();
                    }
                    disableExtraCollisionDisable();
                }
            }
            if (ImGui.BeginItemTooltip())
            {
                ImGui.Text("This will make your legs goofy, but is needed to get on top of some things.");
                ImGui.EndTooltip();
            }
            if (!disableCollision)
            {
                ImGui.PopItemFlag();
                ImGui.PopStyleVar();
            }
            if (!disableExtraCollision)
            {
                ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true);
                ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f);
            }
            if (ImGui.Checkbox("Disable Y Gravity", ref disableGravity))
            {
                if (disableGravity)
                {
                    disableGravityEnable();
                }
                else
                {
                    if (disableExtraGravity)
                    {
                        disableExtraGravity = false;
                        disableExtraGravityDisable();
                    }
                    disableGravityDisable();
                }
            }
            if (ImGui.BeginItemTooltip())
            {
                ImGui.Text("Avoid falling when going too high (Y axis).");
                ImGui.EndTooltip();
            }
            if (!disableExtraCollision)
            {
                ImGui.PopItemFlag();
                ImGui.PopStyleVar();
            }
            if (!disableGravity)
            {
                ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true);
                ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f);
            }
            if (ImGui.Checkbox("Disable More Gravity", ref disableExtraGravity))
            {
                if (disableExtraGravity)
                {
                    disableExtraGravityEnable();
                }
                else
                {
                    disableExtraGravityDisable();
                }
            }
            if (ImGui.BeginItemTooltip())
            {
                ImGui.Text("This can stop you from falling when going out of bounds and/or rotating.");
                ImGui.EndTooltip();
            }
            if (!disableGravity)
            {
                ImGui.PopItemFlag();
                ImGui.PopStyleVar();
            }

            Models.drawAnimationInfo(player, width, false);
        }
    }
}