mirror of
https://github.com/YimMenu/YimMenuV2.git
synced 2026-10-10 11:53:14 +08:00
Update (#505)
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
#include "game/gta/ScriptGlobal.hpp"
|
||||
#include "game/pointers/Pointers.hpp"
|
||||
#include "types/script/globals/GPBD_FM.hpp"
|
||||
#include "types/script/globals/GPBD_FM_2.hpp"
|
||||
|
||||
namespace YimMenu::Features
|
||||
{
|
||||
@@ -13,7 +14,8 @@ namespace YimMenu::Features
|
||||
AGENCY,
|
||||
SALVAGE_YARD,
|
||||
BAIL_OFFICE,
|
||||
GARMENT_FACTORY
|
||||
GARMENT_FACTORY,
|
||||
HANDS_ON_CAR_WASH
|
||||
};
|
||||
|
||||
static std::vector<std::pair<int, const char*>> businessNames = {
|
||||
@@ -22,7 +24,8 @@ namespace YimMenu::Features
|
||||
{static_cast<int>(eAppVinewoodMenuSafe::AGENCY), "Agency"},
|
||||
{static_cast<int>(eAppVinewoodMenuSafe::SALVAGE_YARD), "Salvage Yard"},
|
||||
{static_cast<int>(eAppVinewoodMenuSafe::BAIL_OFFICE), "Bail Office"},
|
||||
{static_cast<int>(eAppVinewoodMenuSafe::GARMENT_FACTORY), "Garment Factory"}};
|
||||
{static_cast<int>(eAppVinewoodMenuSafe::GARMENT_FACTORY), "Garment Factory"},
|
||||
{static_cast<int>(eAppVinewoodMenuSafe::HANDS_ON_CAR_WASH), "Hands on Car Wash"}};
|
||||
|
||||
static ListCommand _SelectedBusiness{"businesssafe", "Business", "Business to claim earning from.", businessNames, 0};
|
||||
|
||||
@@ -58,7 +61,7 @@ namespace YimMenu::Features
|
||||
case eAppVinewoodMenuSafe::SALVAGE_YARD:
|
||||
{
|
||||
if (GPBD_FM::Get()->Entries[Self::GetPlayer().GetId()].PropertyData.SalvageYardData.TotalEarnings > 0)
|
||||
*ScriptGlobal(2708252).As<BOOL*>() = TRUE;
|
||||
*ScriptGlobal(2708228).As<BOOL*>() = TRUE;
|
||||
break;
|
||||
}
|
||||
case eAppVinewoodMenuSafe::BAIL_OFFICE:
|
||||
@@ -70,7 +73,13 @@ namespace YimMenu::Features
|
||||
case eAppVinewoodMenuSafe::GARMENT_FACTORY:
|
||||
{
|
||||
if (GPBD_FM::Get()->Entries[Self::GetPlayer().GetId()].PropertyData.HackerDenData.SafeCashValue > 0)
|
||||
*ScriptGlobal(2708240).As<BOOL*>() = TRUE;
|
||||
*ScriptGlobal(2708246).As<BOOL*>() = TRUE;
|
||||
break;
|
||||
}
|
||||
case eAppVinewoodMenuSafe::HANDS_ON_CAR_WASH:
|
||||
{
|
||||
if (GPBD_FM_2::Get()->Entries[Self::GetPlayer().GetId()].SYVehSaleData.HOWCData.SafeCashValue > 0)
|
||||
*ScriptGlobal(2708259).As<BOOL*>() = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ namespace YimMenu::Features
|
||||
|
||||
static ListCommand animalIndex = {"animalindex", "Animal", "Selected Animal", {{0, "Animal 1"}, {1, "Animal 2"}, {2, "Animal 3"}}};
|
||||
|
||||
static ListCommand productIndex = {"productindex", "Product", "Selected Product", {{0, "Product 1"}, {1, "Product 2"}, {2, "Product 3"}, {3, "Product 4"}, {4, "Product 5"}, {5, "Product 6"}, {6, "Product 7"}, {7, "Product 8"}, {8, "Product 9"}, {9, "Product 10"}}};
|
||||
|
||||
static constexpr auto wildlifePhotographyAnimalHashes = std::to_array({"A_C_Boar"_J, "A_C_Cat_01"_J, "A_C_Cow"_J, "A_C_Coyote"_J, "A_C_Deer"_J, "A_C_Husky"_J, "A_C_MtLion"_J, "A_C_Pig"_J, "A_C_Poodle"_J, "A_C_Pug"_J, "A_C_Rabbit_01"_J, "A_C_Retriever"_J, "A_C_Rottweiler"_J, "A_C_shepherd"_J, "A_C_Westy"_J, "A_C_Chickenhawk"_J, "A_C_Cormorant"_J, "A_C_Crow"_J, "A_C_Hen"_J, "A_C_Seagull"_J});
|
||||
|
||||
static void SetAllDailyActivitiesCompleted(bool completed)
|
||||
@@ -77,6 +79,16 @@ namespace YimMenu::Features
|
||||
Stats::SetPackedBool(42059, completed); // Shoot Animals Photography 1
|
||||
Stats::SetPackedBool(42060, completed); // Shoot Animals Photography 2
|
||||
Stats::SetPackedBool(42061, completed); // Shoot Animals Photography 3
|
||||
Stats::SetPackedBool(54672, completed); // Smoke on the Water Product 1
|
||||
Stats::SetPackedBool(54673, completed); // Smoke on the Water Product 2
|
||||
Stats::SetPackedBool(54674, completed); // Smoke on the Water Product 3
|
||||
Stats::SetPackedBool(54675, completed); // Smoke on the Water Product 4
|
||||
Stats::SetPackedBool(54676, completed); // Smoke on the Water Product 5
|
||||
Stats::SetPackedBool(54677, completed); // Smoke on the Water Product 6
|
||||
Stats::SetPackedBool(54678, completed); // Smoke on the Water Product 7
|
||||
Stats::SetPackedBool(54679, completed); // Smoke on the Water Product 8
|
||||
Stats::SetPackedBool(54680, completed); // Smoke on the Water Product 9
|
||||
Stats::SetPackedBool(54681, completed); // Smoke on the Water Product 10
|
||||
// Street Dealers
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
@@ -192,7 +204,7 @@ namespace YimMenu::Features
|
||||
}
|
||||
}
|
||||
|
||||
auto streetDealerData = ScriptLocal(m_Thread, 253).At(12);
|
||||
auto streetDealerData = ScriptLocal(m_Thread, 256).At(12);
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
@@ -201,7 +213,7 @@ namespace YimMenu::Features
|
||||
FreemodeGeneral::Get()->StreetDealers.Dealers[selected].Location;
|
||||
FreemodeGeneral::Get()->StreetDealers.ClosetsDealerIndex = selected;
|
||||
|
||||
static ScriptFunction initStreetDealerData("fm_street_dealer"_J, ScriptPointer("InitStreetDealerData", "2D 00 07 00 00 61 E9 CE 29"));
|
||||
static ScriptFunction initStreetDealerData("fm_street_dealer"_J, ScriptPointer("InitStreetDealerData", "2D 00 07 00 00 61"));
|
||||
initStreetDealerData.Call<void>();
|
||||
streetDealerData.At(5).As<SCR_BITSET<uint64_t>*>()->Set(0);
|
||||
initialized = true;
|
||||
@@ -643,8 +655,8 @@ namespace YimMenu::Features
|
||||
{
|
||||
thread->m_Context.m_State = rage::scrThread::State::PAUSED;
|
||||
|
||||
*ScriptLocal(thread, 3060).At(130).At(1).As<int*>() = FreemodeGeneral::Get()->DailyReset.Seed % 14; // if we don't init this, the par time duration function will return 0 and the COMPLETED stat will be set to 0, which is bad
|
||||
*ScriptLocal(thread, 142).At(4).As<int*>() = 0;
|
||||
*ScriptLocal(thread, 3088).At(131).At(1).As<int*>() = FreemodeGeneral::Get()->DailyReset.Seed % 14; // if we don't init this, the par time duration function will return 0 and the COMPLETED stat will be set to 0, which is bad
|
||||
*ScriptLocal(thread, 144).At(4).As<int*>() = 0;
|
||||
static ScriptFunction onBTTEnd("fm_content_bicycle_time_trial"_J,
|
||||
ScriptPointer("OnBTTEnd", "64 ? ? ? 5D ? ? ? 75 77").Add(1).Rip());
|
||||
onBTTEnd.Call<void>();
|
||||
@@ -783,8 +795,8 @@ namespace YimMenu::Features
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
int combination = *ScriptLocal(thread, 142).At(22).At(i, 2).At(1).As<int*>();
|
||||
*ScriptLocal(thread, 142).At(22).At(i, 2).As<float*>() = combination;
|
||||
int combination = *ScriptLocal(thread, 144).At(22).At(i, 2).At(1).As<int*>();
|
||||
*ScriptLocal(thread, 144).At(22).At(i, 2).As<float*>() = combination;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -902,7 +914,7 @@ namespace YimMenu::Features
|
||||
|
||||
if (auto thread = Scripts::FindScriptThread("fm_content_daily_bounty"_J))
|
||||
{
|
||||
if (auto coords = *ScriptLocal(thread, 239).At(434).At(1).At(0, 4).As<Vector3*>())
|
||||
if (auto coords = *ScriptLocal(thread, 241).At(434).At(1).At(0, 4).As<Vector3*>())
|
||||
{
|
||||
Self::GetPed().TeleportTo(coords);
|
||||
}
|
||||
@@ -960,6 +972,58 @@ namespace YimMenu::Features
|
||||
}
|
||||
};
|
||||
|
||||
class TeleportToProduct : public Command
|
||||
{
|
||||
using Command::Command;
|
||||
|
||||
virtual void OnCall() override
|
||||
{
|
||||
if (!*Pointers.IsSessionStarted)
|
||||
return;
|
||||
|
||||
if (Stats::GetInt("MPX_SB_WEED_SHOP_OWNED") == 0)
|
||||
{
|
||||
Notifications::Show("Smoke on the Water", "You must own a Smoke on the Water property.", NotificationType::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Stats::GetPackedBool(54672 + productIndex.GetState()))
|
||||
{
|
||||
TeleportToCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables::SmokeOnTheWater, productIndex.GetState());
|
||||
}
|
||||
else
|
||||
{
|
||||
Notifications::Show("Smoke on the Water", "This product has already been collected.", NotificationType::Error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class CollectProduct : public Command
|
||||
{
|
||||
using Command::Command;
|
||||
|
||||
virtual void OnCall() override
|
||||
{
|
||||
if (!*Pointers.IsSessionStarted)
|
||||
return;
|
||||
|
||||
if (Stats::GetInt("MPX_SB_WEED_SHOP_OWNED") == 0)
|
||||
{
|
||||
Notifications::Show("Smoke on the Water", "You must own a Smoke on the Water property.", NotificationType::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Stats::GetPackedBool(54672 + productIndex.GetState()))
|
||||
{
|
||||
CollectCollectable(SCRIPT_EVENT_COLLECT_COLLECTABLE::eCollectables::SmokeOnTheWater, productIndex.GetState());
|
||||
}
|
||||
else
|
||||
{
|
||||
Notifications::Show("Smoke on the Water", "This product has already been collected.", NotificationType::Error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static SetAllActivitiesCompleted _SetAllActivitiesCompleted{"setallactivitiescompleted", "Set All Activities Completed", "Switch session to apply the changes."};
|
||||
static ResetAllActivities _ResetAllActivities{"resetallactivities", "Reset All Activities", "Switch session to apply the changes."};
|
||||
|
||||
@@ -1005,4 +1069,7 @@ namespace YimMenu::Features
|
||||
|
||||
static SpawnAnimal _SpawnAnimal{"spawnanimal", "Spawn Animal", "Spawns the selected animal."};
|
||||
static PhotographAnimal _PhotographAnimal{"photographanimal", "Photograph Animal", "Photographs the selected animal."};
|
||||
|
||||
static TeleportToProduct _TeleportToProduct{"tptoproduct", "Teleport to Product", "Teleports to the selected product."};
|
||||
static CollectProduct _CollectProduct{"collectproduct", "Collect Product", "Collects the selected product."};
|
||||
}
|
||||
@@ -33,7 +33,7 @@ namespace YimMenu::Features
|
||||
m_Thread->m_Context.m_State = rage::scrThread::State::PAUSED;
|
||||
if (!skipGunLockerMenuChecksPatch)
|
||||
{
|
||||
skipGunLockerMenuChecksPatch = ScriptPatches::AddPatch("AM_MP_AUTO_SHOP"_J, ScriptPointer("SkipGunLockerMenuChecksPatch", "56 08 01 2C 01 03 A5"), {0x55, 0xB1, 0x00});
|
||||
skipGunLockerMenuChecksPatch = ScriptPatches::AddPatch("AM_MP_AUTO_SHOP"_J, ScriptPointer("SkipGunLockerMenuChecksPatch", "72 5D ? ? ? 56 ? ? 2C ? ? ? 2C ? ? ? 06").Add(5), {0x55, 0xB1, 0x00});
|
||||
}
|
||||
skipGunLockerMenuChecksPatch->Enable();
|
||||
}
|
||||
@@ -45,7 +45,7 @@ namespace YimMenu::Features
|
||||
}
|
||||
|
||||
static ScriptFunction runGunLockerMenu("AM_MP_AUTO_SHOP"_J, ScriptPointer("RunGunLockerMenu", "2D 06 08 00 00 38 03 5D ? ? ? 57 03 00"));
|
||||
auto gunLockerData = ScriptLocal(m_Thread, 290).At(569);
|
||||
auto gunLockerData = ScriptLocal(m_Thread, 292).At(583);
|
||||
|
||||
int unused;
|
||||
runGunLockerMenu.Call<void>(gunLockerData.As<void*>(), &unused, eSimpleInteriorIndex::SIMPLE_INTERIOR_AUTO_SHOP_MISSION_ROW, Self::GetPlayer().GetId(), false, true);
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace YimMenu::Features
|
||||
|
||||
if (!shouldCleanupWardrobePatch)
|
||||
{
|
||||
shouldCleanupWardrobePatch = ScriptPatches::AddPatch("wardrobe_mp"_J, ScriptPointer("ShouldCleanupWardrobePatch", "2C 01 00 40 2C 05 02 69 06 56 1C 07"), {0x72, 0x2E, 0x05, 0x01});
|
||||
shouldCleanupWardrobePatch = ScriptPatches::AddPatch("wardrobe_mp"_J, ScriptPointer("ShouldCleanupWardrobePatch", "2C ? ? ? 2C ? ? ? 06 56 ? ? 2C ? ? ? 56 ? ? 2C"), {0x72, 0x2E, 0x05, 0x01});
|
||||
}
|
||||
shouldCleanupWardrobePatch->Enable();
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace YimMenu::Features
|
||||
{
|
||||
if (auto thread = Scripts::FindScriptThread("wardrobe_mp"_J))
|
||||
{
|
||||
if (*ScriptLocal(thread, 187).At(46).At(9).As<int*>() == 4)
|
||||
if (*ScriptLocal(thread, 189).At(46).At(9).As<int*>() == 4)
|
||||
{
|
||||
shouldCleanupWardrobePatch->Disable();
|
||||
isWardrobeOpenedPatch->Disable();
|
||||
|
||||
@@ -24,8 +24,8 @@ namespace YimMenu::Features
|
||||
|
||||
if (auto freemode = Scripts::FindScriptThread("freemode"_J))
|
||||
{
|
||||
auto data = ScriptLocal(freemode, 8732);
|
||||
*data.At(3).As<int*>() = 215;
|
||||
auto data = ScriptLocal(freemode, 8748);
|
||||
*data.At(3).As<int*>() = 223;
|
||||
*data.At(3).At(16).As<int*>() = Self::GetPlayer().GetId();
|
||||
*data.At(3).At(1).As<int*>() = 89;
|
||||
*data.At(2).As<int*>() = "AM_CONTACT_REQUESTS"_J;
|
||||
@@ -35,7 +35,7 @@ namespace YimMenu::Features
|
||||
{
|
||||
if (auto thread = Scripts::FindScriptThreadByID(id))
|
||||
{
|
||||
*ScriptLocal(thread, 519).As<int*>() = 1;
|
||||
*ScriptLocal(thread, 521).As<int*>() = 1;
|
||||
ScriptGlobal(2685690).At(4347).At(254).At(7, 2).As<TIMER*>()->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace YimMenu::Submenus
|
||||
auto lsTagsTab = std::make_shared<CollapsingHeaderItem>("LS Tags");
|
||||
auto madrazoHitsTab = std::make_shared<CollapsingHeaderItem>("Madrazo Hits");
|
||||
auto wildlifePhotography = std::make_shared<CollapsingHeaderItem>("Shoot Animals Photography");
|
||||
auto smokeOnTheWater = std::make_shared<CollapsingHeaderItem>("Smoke on the Water");
|
||||
|
||||
dailyActivities->AddItem(std::make_shared<CommandItem>("setallactivitiescompleted"_J));
|
||||
dailyActivities->AddItem(std::make_shared<CommandItem>("resetallactivities"_J));
|
||||
@@ -76,6 +77,10 @@ namespace YimMenu::Submenus
|
||||
wildlifePhotography->AddItem(std::make_shared<CommandItem>("spawnanimal"_J));
|
||||
wildlifePhotography->AddItem(std::make_shared<CommandItem>("photographanimal"_J));
|
||||
|
||||
smokeOnTheWater->AddItem(std::make_shared<ListCommandItem>("productindex"_J));
|
||||
smokeOnTheWater->AddItem(std::make_shared<CommandItem>("tptoproduct"_J));
|
||||
smokeOnTheWater->AddItem(std::make_shared<CommandItem>("collectproduct"_J));
|
||||
|
||||
dailyActivities->AddItem(std::move(challengesTab));
|
||||
dailyActivities->AddItem(std::move(hiddenCacheTab));
|
||||
dailyActivities->AddItem(std::move(treasureChestTab));
|
||||
@@ -90,6 +95,7 @@ namespace YimMenu::Submenus
|
||||
dailyActivities->AddItem(std::move(lsTagsTab));
|
||||
dailyActivities->AddItem(std::move(madrazoHitsTab));
|
||||
dailyActivities->AddItem(std::move(wildlifePhotography));
|
||||
dailyActivities->AddItem(std::move(smokeOnTheWater));
|
||||
return dailyActivities;
|
||||
}
|
||||
}
|
||||
@@ -217,6 +217,7 @@ struct SCRIPT_EVENT_COLLECT_COLLECTABLE : public SCRIPT_EVENT
|
||||
Tagging = 19,
|
||||
SprayCans = 20,
|
||||
Yuanbao = 21,
|
||||
SmokeOnTheWater = 22
|
||||
};
|
||||
|
||||
REGISTER_SCRIPT_EVENT(SCRIPT_EVENT_COLLECT_COLLECTABLE, CollectCollectable);
|
||||
|
||||
@@ -58,12 +58,12 @@ struct FreemodeGeneral
|
||||
{
|
||||
uint64_t PAD_0000[301];
|
||||
VEHICLE_INDEX PersonalVehicleIndex; // 301
|
||||
uint64_t PAD_0302[6551];
|
||||
STREET_DEALERS StreetDealers; // 6853
|
||||
uint64_t PAD_6877[57];
|
||||
DAILY_RESET_DATA DailyReset; // 6934
|
||||
FREEMODE_DELIVERY_DATA DeliveryData; // 6938
|
||||
uint64_t PAD_0302[6553];
|
||||
STREET_DEALERS StreetDealers; // 6855
|
||||
uint64_t PAD_6879[71];
|
||||
DAILY_RESET_DATA DailyReset; // 6950
|
||||
FREEMODE_DELIVERY_DATA DeliveryData; // 6954
|
||||
|
||||
static FreemodeGeneral* Get();
|
||||
};
|
||||
//static_assert(sizeof(FreemodeGeneral) == 7224 * 8);
|
||||
//static_assert(sizeof(FreemodeGeneral) == 7240 * 8);
|
||||
@@ -73,6 +73,22 @@ struct RANDOM_EVENTS_CLIENT_DATA
|
||||
};
|
||||
static_assert(sizeof(RANDOM_EVENTS_CLIENT_DATA) == 67 * 8);
|
||||
|
||||
struct HOCW_DATA
|
||||
{
|
||||
uint64_t PAD_0000[2];
|
||||
SCR_INT SafeCashValue; // 2
|
||||
uint64_t PAD_0003[13];
|
||||
};
|
||||
static_assert(sizeof(HOCW_DATA) == 16 * 8);
|
||||
|
||||
struct SALVAGE_YARD_VEHICLE_SALE_DATA
|
||||
{
|
||||
uint64_t PAD_0000[27];
|
||||
HOCW_DATA HOWCData; // 27
|
||||
uint64_t PAD_0043[9];
|
||||
};
|
||||
static_assert(sizeof(SALVAGE_YARD_VEHICLE_SALE_DATA) == 52 * 8);
|
||||
|
||||
struct GPBD_FM_2_Entry
|
||||
{
|
||||
SCR_INT NumTeams; // 0
|
||||
@@ -108,7 +124,7 @@ struct GPBD_FM_2_Entry
|
||||
SCR_VEC3 CoronaPosition; // 40
|
||||
GANGOPS_DATA GangopsData; // 43
|
||||
RANDOM_EVENTS_CLIENT_DATA RandomEventsClientData; // 82
|
||||
uint64_t PAD_0149[52]; // 149 added b889g9, salvage yard vehicle sale data
|
||||
SALVAGE_YARD_VEHICLE_SALE_DATA SYVehSaleData; // 149 added b889g9
|
||||
};
|
||||
static_assert(sizeof(GPBD_FM_2_Entry) == 201 * 8);
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
DEFINE_GLOBAL_ACCESSOR(GlobalPlayerBD, 2658019);
|
||||
DEFINE_GLOBAL_ACCESSOR(GPBD_FM, 1845274);
|
||||
DEFINE_GLOBAL_ACCESSOR(GPBD_FM_2, 1845274);
|
||||
DEFINE_GLOBAL_ACCESSOR(GPBD_FM_2, 1882449);
|
||||
DEFINE_GLOBAL_ACCESSOR(GPBD_FM_3, 1888882);
|
||||
DEFINE_GLOBAL_ACCESSOR(GPBD_Flow, 1980192);
|
||||
DEFINE_GLOBAL_ACCESSOR(GPBD_Kicking, 1873852);
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
}
|
||||
|
||||
DEFINE_LOCAL_ACCESSOR(VEHICLE_REWARD_DATA, 129);
|
||||
DEFINE_LOCAL_ACCESSOR(RANDOM_EVENTS_FREEMODE_DATA, 15919);
|
||||
DEFINE_LOCAL_ACCESSOR(StandardTimeTrialData, 14568);
|
||||
DEFINE_LOCAL_ACCESSOR(RCBanditoTimeTrialData, 14618);
|
||||
DEFINE_LOCAL_ACCESSOR(HSWTimeTrialData, 14662);
|
||||
DEFINE_LOCAL_ACCESSOR(BikeTimeTrialData, 15429);
|
||||
DEFINE_LOCAL_ACCESSOR(RANDOM_EVENTS_FREEMODE_DATA, 16019);
|
||||
DEFINE_LOCAL_ACCESSOR(StandardTimeTrialData, 14640);
|
||||
DEFINE_LOCAL_ACCESSOR(RCBanditoTimeTrialData, 14690);
|
||||
DEFINE_LOCAL_ACCESSOR(HSWTimeTrialData, 14734);
|
||||
DEFINE_LOCAL_ACCESSOR(BikeTimeTrialData, 15529);
|
||||
Reference in New Issue
Block a user