mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-12-21 23:58:51 -05:00
[GameInteractor] Game Hooks (#2481)
This commit is contained in:
parent
2c10bca615
commit
8934274c67
@ -258,8 +258,14 @@ set(Header_Files__soh__Enhancements__item_tables
|
||||
|
||||
source_group("Header Files\\soh\\Enhancements\\item-tables" FILES ${Header_Files__soh__Enhancements__item_tables})
|
||||
|
||||
set(Header_Files__soh__Enhancements__mods
|
||||
"soh/Enhancements/mods/modhooks.h"
|
||||
)
|
||||
source_group("Header Files\\soh\\Enhancements\\mods" FILES ${Header_Files__soh__Enhancements__mods})
|
||||
|
||||
set(Header_Files__soh__Enhancements__game_interactor
|
||||
"soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
"soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
"soh/Enhancements/game-interactor/GameInteractionEffect.h"
|
||||
)
|
||||
source_group("Header Files\\soh\\Enhancements\\game-interactor" FILES ${Header_Files__soh__Enhancements__game_interactor})
|
||||
@ -609,9 +615,15 @@ set(Source_Files__soh__Enhancements__item_tables
|
||||
|
||||
source_group("Source Files\\soh\\Enhancements\\item-tables" FILES ${Source_Files__soh__Enhancements__item_tables})
|
||||
|
||||
set(Source_Files__soh__Enhancements__mods
|
||||
"soh/Enhancements/mods/modhooks.cpp"
|
||||
)
|
||||
source_group("Source Files\\soh\\Enhancements\\mods" FILES ${Source_Files__soh__Enhancements__mods})
|
||||
|
||||
set(Source_Files__soh__Enhancements__game_interactor
|
||||
"soh/Enhancements/game-interactor/GameInteractor.cpp"
|
||||
"soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp"
|
||||
"soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp"
|
||||
"soh/Enhancements/game-interactor/GameInteractor_State.cpp"
|
||||
"soh/Enhancements/game-interactor/GameInteractionEffect.cpp"
|
||||
)
|
||||
@ -1835,6 +1847,7 @@ set(ALL_FILES
|
||||
${Header_Files__soh__Enhancements__randomizer__3drando}
|
||||
${Header_Files__soh__Enhancements__item_tables}
|
||||
${Header_Files__soh__Enhancements__custom_message}
|
||||
${Header_Files__soh__Enhancements__mods}
|
||||
${Header_Files__soh__Enhancements__game_interactor}
|
||||
${Header_Files__soh__Enhancements__crowd_control}
|
||||
${Source_Files__soh}
|
||||
@ -1849,6 +1862,7 @@ set(ALL_FILES
|
||||
${Source_Files__soh__Enhancements__randomizer__3drando__location_access}
|
||||
${Source_Files__soh__Enhancements__item_tables}
|
||||
${Source_Files__soh__Enhancements__custom_message}
|
||||
${Source_Files__soh__Enhancements__mods}
|
||||
${Source_Files__soh__Enhancements__game_interactor}
|
||||
${Source_Files__soh__Enhancements__crowd_control}
|
||||
${Source_Files__src__boot}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#define GameInteractor_h
|
||||
|
||||
#include "GameInteractionEffect.h"
|
||||
#include "z64.h"
|
||||
|
||||
typedef enum {
|
||||
/* 0x00 */ GI_LINK_SIZE_NORMAL,
|
||||
@ -39,6 +40,15 @@ GIGravityLevel GameInteractor_GravityLevel();
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#define DEFINE_HOOK(name, type) \
|
||||
struct name { \
|
||||
typedef std::function<type> fn; \
|
||||
}
|
||||
|
||||
class GameInteractor {
|
||||
public:
|
||||
static GameInteractor* Instance;
|
||||
@ -65,6 +75,23 @@ public:
|
||||
static GameInteractionEffectQueryResult ApplyEffect(GameInteractionEffectBase* effect);
|
||||
static GameInteractionEffectQueryResult RemoveEffect(GameInteractionEffectBase* effect);
|
||||
|
||||
// Game Hooks
|
||||
template <typename H> struct RegisteredGameHooks { inline static std::vector<typename H::fn> functions; };
|
||||
template <typename H> void RegisterGameHook(typename H::fn h) { RegisteredGameHooks<H>::functions.push_back(h); }
|
||||
template <typename H, typename... Args> void ExecuteHooks(Args&&... args) {
|
||||
for (auto& fn : RegisteredGameHooks<H>::functions) {
|
||||
fn(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_HOOK(OnReceiveItem, void(u8 item));
|
||||
DEFINE_HOOK(OnSceneInit, void(s16 sceneNum));
|
||||
|
||||
|
||||
DEFINE_HOOK(OnSaveFile, void(int fileNum));
|
||||
DEFINE_HOOK(OnLoadFile, void(int fileNum));
|
||||
DEFINE_HOOK(OnDeleteFile, void(int fileNum));
|
||||
|
||||
// Helpers
|
||||
static bool IsSaveLoaded();
|
||||
static bool IsGameplayPaused();
|
||||
|
@ -0,0 +1,29 @@
|
||||
#include "GameInteractor_Hooks.h"
|
||||
|
||||
extern "C" {
|
||||
extern PlayState* gPlayState;
|
||||
}
|
||||
|
||||
// MARK: - Gameplay
|
||||
|
||||
void GameInteractor_ExecuteOnReceiveItemHooks(u8 item) {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnReceiveItem>(item);
|
||||
}
|
||||
|
||||
void GameInteractor_ExecuteOnSceneInitHooks(s16 sceneNum) {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSceneInit>(sceneNum);
|
||||
}
|
||||
|
||||
// MARK: - Save Files
|
||||
|
||||
void GameInteractor_ExecuteOnSaveFile(int fileNum) {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSaveFile>(fileNum);
|
||||
}
|
||||
|
||||
void GameInteractor_ExecuteOnLoadFile(int fileNum) {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnLoadFile>(fileNum);
|
||||
}
|
||||
|
||||
void GameInteractor_ExecuteOnDeleteFile(int fileNum) {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnDeleteFile>(fileNum);
|
||||
}
|
10
soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h
Normal file
10
soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h
Normal file
@ -0,0 +1,10 @@
|
||||
#include "GameInteractor.h"
|
||||
|
||||
// MARK: - Gameplay
|
||||
extern "C" void GameInteractor_ExecuteOnReceiveItemHooks(u8 item);
|
||||
extern "C" void GameInteractor_ExecuteOnSceneInit(s16 sceneNum);
|
||||
|
||||
// MARK: - Save Files
|
||||
extern "C" void GameInteractor_ExecuteOnSaveFile(int fileNum);
|
||||
extern "C" void GameInteractor_ExecuteOnLoadFile(int fileNum);
|
||||
extern "C" void GameInteractor_ExecuteOnDeleteFile(int fileNum);
|
68
soh/soh/Enhancements/mods/modhooks.cpp
Normal file
68
soh/soh/Enhancements/mods/modhooks.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include <libultraship/bridge.h>
|
||||
|
||||
extern "C" {
|
||||
#include <z64.h>
|
||||
extern SaveContext gSaveContext;
|
||||
extern PlayState* gPlayState;
|
||||
extern void Play_PerformSave(PlayState* play);
|
||||
}
|
||||
|
||||
void RegisterAutoSaveOnReceiveItem() {
|
||||
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnReceiveItem>([](u8 item) {
|
||||
if (CVarGetInteger("gAutosave", 0) && (gPlayState != NULL) && (gPlayState->sceneNum != SCENE_KENJYANOMA) && (gSaveContext.pendingSale == ITEM_NONE) && (gPlayState->sceneNum != SCENE_GANON_DEMO)) {
|
||||
if (CVarGetInteger("gAutosaveAllItems", 0)) {
|
||||
Play_PerformSave(gPlayState);
|
||||
} else if (CVarGetInteger("gAutosaveMajorItems", 1)) {
|
||||
switch (item) {
|
||||
case ITEM_STICK:
|
||||
case ITEM_NUT:
|
||||
case ITEM_BOMB:
|
||||
case ITEM_BOW:
|
||||
case ITEM_SEEDS:
|
||||
case ITEM_FISHING_POLE:
|
||||
case ITEM_MAGIC_SMALL:
|
||||
case ITEM_MAGIC_LARGE:
|
||||
case ITEM_INVALID_4:
|
||||
case ITEM_INVALID_5:
|
||||
case ITEM_INVALID_6:
|
||||
case ITEM_INVALID_7:
|
||||
case ITEM_HEART:
|
||||
case ITEM_RUPEE_GREEN:
|
||||
case ITEM_RUPEE_BLUE:
|
||||
case ITEM_RUPEE_RED:
|
||||
case ITEM_RUPEE_PURPLE:
|
||||
case ITEM_RUPEE_GOLD:
|
||||
case ITEM_INVALID_8:
|
||||
case ITEM_STICKS_5:
|
||||
case ITEM_STICKS_10:
|
||||
case ITEM_NUTS_5:
|
||||
case ITEM_NUTS_10:
|
||||
case ITEM_BOMBS_5:
|
||||
case ITEM_BOMBS_10:
|
||||
case ITEM_BOMBS_20:
|
||||
case ITEM_BOMBS_30:
|
||||
case ITEM_ARROWS_SMALL:
|
||||
case ITEM_ARROWS_MEDIUM:
|
||||
case ITEM_ARROWS_LARGE:
|
||||
case ITEM_SEEDS_30:
|
||||
break;
|
||||
case ITEM_BOMBCHU:
|
||||
case ITEM_BOMBCHUS_5:
|
||||
case ITEM_BOMBCHUS_20:
|
||||
if (!CVarGetInteger("gBombchuDrops", 0)) {
|
||||
Play_PerformSave(gPlayState);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Play_PerformSave(gPlayState);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" void RegisterModHooks() {
|
||||
RegisterAutoSaveOnReceiveItem();
|
||||
}
|
5
soh/soh/Enhancements/mods/modhooks.h
Normal file
5
soh/soh/Enhancements/mods/modhooks.h
Normal file
@ -0,0 +1,5 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
void RegisterModHooks(void);
|
||||
}
|
||||
#endif
|
@ -73,8 +73,9 @@
|
||||
CrowdControl* CrowdControl::Instance;
|
||||
#endif
|
||||
|
||||
#include "Enhancements/mods/modhooks.h"
|
||||
#include "Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "libultraship/libultraship.h"
|
||||
#include <libultraship/libultraship.h>
|
||||
|
||||
// Resource Types/Factories
|
||||
#include "soh/resource/type/Animation.h"
|
||||
@ -584,6 +585,8 @@ extern "C" void InitOTR() {
|
||||
OTRExtScanner();
|
||||
VanillaItemTable_Init();
|
||||
|
||||
RegisterModHooks();
|
||||
|
||||
time_t now = time(NULL);
|
||||
tm *tm_now = localtime(&now);
|
||||
if (tm_now->tm_mon == 11 && tm_now->tm_mday >= 24 && tm_now->tm_mday <= 25) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "SaveManager.h"
|
||||
#include "OTRGlobals.h"
|
||||
#include "Enhancements/game-interactor/GameInteractor.h"
|
||||
|
||||
#include "z64.h"
|
||||
#include "functions.h"
|
||||
@ -693,6 +694,7 @@ void SaveManager::SaveFile(int fileNum) {
|
||||
#endif
|
||||
|
||||
InitMeta(fileNum);
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSaveFile>(fileNum);
|
||||
}
|
||||
|
||||
void SaveManager::SaveGlobal() {
|
||||
@ -754,6 +756,7 @@ void SaveManager::LoadFile(int fileNum) {
|
||||
break;
|
||||
}
|
||||
InitMeta(fileNum);
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnLoadFile>(fileNum);
|
||||
}
|
||||
|
||||
bool SaveManager::SaveFile_Exist(int fileNum) {
|
||||
@ -1680,6 +1683,7 @@ void SaveManager::DeleteZeldaFile(int fileNum) {
|
||||
}
|
||||
fileMetaInfo[fileNum].valid = false;
|
||||
fileMetaInfo[fileNum].randoSave = false;
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnDeleteFile>(fileNum);
|
||||
}
|
||||
|
||||
bool SaveManager::IsRandoFile() {
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <ResourceMgr.h>
|
||||
#include "soh/resource/type/Scene.h"
|
||||
#include <Utils/StringHelper.h>
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "global.h"
|
||||
#include "vt.h"
|
||||
#include <Vertex.h>
|
||||
@ -82,5 +83,7 @@ void OTRPlay_InitScene(PlayState* play, s32 spawn) {
|
||||
|
||||
auto data2 = ResourceMgr_LoadVtxByCRC(0x68d4ea06044e228f);*/
|
||||
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSceneInit>(play->sceneNum);
|
||||
|
||||
volatile int a = 0;
|
||||
}
|
||||
|
@ -1738,7 +1738,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
func_8006D0AC(play);
|
||||
}
|
||||
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if ((item >= ITEM_SONG_MINUET) && (item <= ITEM_SONG_STORMS)) {
|
||||
gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_SONG_MINUET + QUEST_SONG_MINUET];
|
||||
@ -1750,7 +1750,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
gBitFlags[item - ITEM_SONG_MINUET + QUEST_SONG_MINUET], gBitFlags[item - ITEM_SONG_MINUET]);
|
||||
osSyncPrintf(VT_RST);
|
||||
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if ((item >= ITEM_KOKIRI_EMERALD) && (item <= ITEM_ZORA_SAPPHIRE)) {
|
||||
gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_KOKIRI_EMERALD + QUEST_KOKIRI_EMERALD];
|
||||
@ -1759,7 +1759,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
osSyncPrintf("精霊石 = %x\n", gSaveContext.inventory.questItems); // "Spiritual Stones = %x"
|
||||
osSyncPrintf(VT_RST);
|
||||
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if ((item == ITEM_STONE_OF_AGONY) || (item == ITEM_GERUDO_CARD)) {
|
||||
gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_STONE_OF_AGONY + QUEST_STONE_OF_AGONY];
|
||||
@ -1768,7 +1768,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
osSyncPrintf("アイテム = %x\n", gSaveContext.inventory.questItems); // "Items = %x"
|
||||
osSyncPrintf(VT_RST);
|
||||
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_SKULL_TOKEN) {
|
||||
gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_SKULL_TOKEN + QUEST_SKULL_TOKEN];
|
||||
@ -1779,7 +1779,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
osSyncPrintf("Nコイン = %x(%d)\n", gSaveContext.inventory.questItems, gSaveContext.inventory.gsTokens);
|
||||
osSyncPrintf(VT_RST);
|
||||
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if ((item >= ITEM_SWORD_KOKIRI) && (item <= ITEM_SWORD_BGS)) {
|
||||
gSaveContext.inventory.equipment |= gBitFlags[item - ITEM_SWORD_KOKIRI] << gEquipShifts[EQUIP_SWORD];
|
||||
@ -1811,19 +1811,19 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
}
|
||||
}
|
||||
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if ((item >= ITEM_SHIELD_DEKU) && (item <= ITEM_SHIELD_MIRROR)) {
|
||||
gSaveContext.inventory.equipment |= (gBitFlags[item - ITEM_SHIELD_DEKU] << gEquipShifts[EQUIP_SHIELD]);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if ((item >= ITEM_TUNIC_KOKIRI) && (item <= ITEM_TUNIC_ZORA)) {
|
||||
gSaveContext.inventory.equipment |= (gBitFlags[item - ITEM_TUNIC_KOKIRI] << gEquipShifts[EQUIP_TUNIC]);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if ((item >= ITEM_BOOTS_KOKIRI) && (item <= ITEM_BOOTS_HOVER)) {
|
||||
gSaveContext.inventory.equipment |= (gBitFlags[item - ITEM_BOOTS_KOKIRI] << gEquipShifts[EQUIP_BOOTS]);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if ((item == ITEM_KEY_BOSS) || (item == ITEM_COMPASS) || (item == ITEM_DUNGEON_MAP)) {
|
||||
// Boss Key, Compass, and Dungeon Map exceptions for rando.
|
||||
@ -1840,7 +1840,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
} else {
|
||||
gSaveContext.inventory.dungeonItems[gSaveContext.mapIndex] |= gBitFlags[item - ITEM_KEY_BOSS];
|
||||
}
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_KEY_SMALL) {
|
||||
// Small key exceptions for rando with keysanity off.
|
||||
@ -1851,11 +1851,11 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
gSaveContext.sohStats.dungeonKeys[13]++;
|
||||
if (gSaveContext.inventory.dungeonKeys[13] < 0) {
|
||||
gSaveContext.inventory.dungeonKeys[13] = 1;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else {
|
||||
gSaveContext.inventory.dungeonKeys[13]++;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
}
|
||||
}
|
||||
@ -1864,11 +1864,11 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
gSaveContext.sohStats.dungeonKeys[6]++;
|
||||
if (gSaveContext.inventory.dungeonKeys[6] < 0) {
|
||||
gSaveContext.inventory.dungeonKeys[6] = 1;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else {
|
||||
gSaveContext.inventory.dungeonKeys[6]++;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
}
|
||||
}
|
||||
@ -1876,11 +1876,11 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
gSaveContext.sohStats.dungeonKeys[gSaveContext.mapIndex]++;
|
||||
if (gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] < 0) {
|
||||
gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] = 1;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else {
|
||||
gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex]++;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
}
|
||||
} else if ((item == ITEM_QUIVER_30) || (item == ITEM_BOW)) {
|
||||
@ -1888,7 +1888,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
Inventory_ChangeUpgrade(UPG_QUIVER, 1);
|
||||
INV_CONTENT(ITEM_BOW) = ITEM_BOW;
|
||||
AMMO(ITEM_BOW) = CAPACITY(UPG_QUIVER, 1);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else {
|
||||
AMMO(ITEM_BOW)++;
|
||||
@ -1899,29 +1899,29 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
} else if (item == ITEM_QUIVER_40) {
|
||||
Inventory_ChangeUpgrade(UPG_QUIVER, 2);
|
||||
AMMO(ITEM_BOW) = CAPACITY(UPG_QUIVER, 2);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_QUIVER_50) {
|
||||
Inventory_ChangeUpgrade(UPG_QUIVER, 3);
|
||||
AMMO(ITEM_BOW) = CAPACITY(UPG_QUIVER, 3);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_BULLET_BAG_40) {
|
||||
Inventory_ChangeUpgrade(UPG_BULLET_BAG, 2);
|
||||
AMMO(ITEM_SLINGSHOT) = CAPACITY(UPG_BULLET_BAG, 2);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_BULLET_BAG_50) {
|
||||
Inventory_ChangeUpgrade(UPG_BULLET_BAG, 3);
|
||||
AMMO(ITEM_SLINGSHOT) = CAPACITY(UPG_BULLET_BAG, 3);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_BOMB_BAG_20) {
|
||||
if (CUR_UPG_VALUE(UPG_BOMB_BAG) == 0) {
|
||||
Inventory_ChangeUpgrade(UPG_BOMB_BAG, 1);
|
||||
INV_CONTENT(ITEM_BOMB) = ITEM_BOMB;
|
||||
AMMO(ITEM_BOMB) = CAPACITY(UPG_BOMB_BAG, 1);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else {
|
||||
AMMO(ITEM_BOMB)++;
|
||||
@ -1932,46 +1932,46 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
} else if (item == ITEM_BOMB_BAG_30) {
|
||||
Inventory_ChangeUpgrade(UPG_BOMB_BAG, 2);
|
||||
AMMO(ITEM_BOMB) = CAPACITY(UPG_BOMB_BAG, 2);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_BOMB_BAG_40) {
|
||||
Inventory_ChangeUpgrade(UPG_BOMB_BAG, 3);
|
||||
AMMO(ITEM_BOMB) = CAPACITY(UPG_BOMB_BAG, 3);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_BRACELET) {
|
||||
Inventory_ChangeUpgrade(UPG_STRENGTH, 1);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_GAUNTLETS_SILVER) {
|
||||
Inventory_ChangeUpgrade(UPG_STRENGTH, 2);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_GAUNTLETS_GOLD) {
|
||||
Inventory_ChangeUpgrade(UPG_STRENGTH, 3);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_SCALE_SILVER) {
|
||||
Inventory_ChangeUpgrade(UPG_SCALE, 1);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_SCALE_GOLDEN) {
|
||||
Inventory_ChangeUpgrade(UPG_SCALE, 2);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_WALLET_ADULT) {
|
||||
Inventory_ChangeUpgrade(UPG_WALLET, 1);
|
||||
if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_FULL_WALLETS)) {
|
||||
Rupees_ChangeBy(200);
|
||||
}
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_WALLET_GIANT) {
|
||||
Inventory_ChangeUpgrade(UPG_WALLET, 2);
|
||||
if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_FULL_WALLETS)) {
|
||||
Rupees_ChangeBy(500);
|
||||
}
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_STICK_UPGRADE_20) {
|
||||
if (gSaveContext.inventory.items[slot] == ITEM_NONE) {
|
||||
@ -1979,7 +1979,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
}
|
||||
Inventory_ChangeUpgrade(UPG_STICKS, 2);
|
||||
AMMO(ITEM_STICK) = CAPACITY(UPG_STICKS, 2);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_STICK_UPGRADE_30) {
|
||||
if (gSaveContext.inventory.items[slot] == ITEM_NONE) {
|
||||
@ -1987,7 +1987,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
}
|
||||
Inventory_ChangeUpgrade(UPG_STICKS, 3);
|
||||
AMMO(ITEM_STICK) = CAPACITY(UPG_STICKS, 3);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_NUT_UPGRADE_30) {
|
||||
if (gSaveContext.inventory.items[slot] == ITEM_NONE) {
|
||||
@ -1995,7 +1995,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
}
|
||||
Inventory_ChangeUpgrade(UPG_NUTS, 2);
|
||||
AMMO(ITEM_NUT) = CAPACITY(UPG_NUTS, 2);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_NUT_UPGRADE_40) {
|
||||
if (gSaveContext.inventory.items[slot] == ITEM_NONE) {
|
||||
@ -2003,7 +2003,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
}
|
||||
Inventory_ChangeUpgrade(UPG_NUTS, 3);
|
||||
AMMO(ITEM_NUT) = CAPACITY(UPG_NUTS, 3);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_LONGSHOT) {
|
||||
INV_CONTENT(item) = item;
|
||||
@ -2037,7 +2037,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
}
|
||||
}
|
||||
}
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_STICK) {
|
||||
if (gSaveContext.inventory.items[slot] == ITEM_NONE) {
|
||||
@ -2090,40 +2090,40 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
if ((AMMO(ITEM_BOMB) += 1) > CUR_CAPACITY(UPG_BOMB_BAG)) {
|
||||
AMMO(ITEM_BOMB) = CUR_CAPACITY(UPG_BOMB_BAG);
|
||||
}
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if ((item >= ITEM_BOMBS_5) && (item <= ITEM_BOMBS_30)) {
|
||||
if ((AMMO(ITEM_BOMB) += sAmmoRefillCounts[item - ITEM_BOMBS_5]) > CUR_CAPACITY(UPG_BOMB_BAG)) {
|
||||
AMMO(ITEM_BOMB) = CUR_CAPACITY(UPG_BOMB_BAG);
|
||||
}
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_BOMBCHU) {
|
||||
if (gSaveContext.inventory.items[slot] == ITEM_NONE) {
|
||||
INV_CONTENT(ITEM_BOMBCHU) = ITEM_BOMBCHU;
|
||||
AMMO(ITEM_BOMBCHU) = 10;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else {
|
||||
AMMO(ITEM_BOMBCHU) += 10;
|
||||
if (AMMO(ITEM_BOMBCHU) > 50) {
|
||||
AMMO(ITEM_BOMBCHU) = 50;
|
||||
}
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
}
|
||||
} else if ((item == ITEM_BOMBCHUS_5) || (item == ITEM_BOMBCHUS_20)) {
|
||||
if (gSaveContext.inventory.items[slot] == ITEM_NONE) {
|
||||
INV_CONTENT(ITEM_BOMBCHU) = ITEM_BOMBCHU;
|
||||
AMMO(ITEM_BOMBCHU) += sAmmoRefillCounts[item - ITEM_BOMBCHUS_5 + 8];
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else {
|
||||
AMMO(ITEM_BOMBCHU) += sAmmoRefillCounts[item - ITEM_BOMBCHUS_5 + 8];
|
||||
if (AMMO(ITEM_BOMBCHU) > 50) {
|
||||
AMMO(ITEM_BOMBCHU) = 50;
|
||||
}
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
}
|
||||
} else if ((item >= ITEM_ARROWS_SMALL) && (item <= ITEM_ARROWS_LARGE)) {
|
||||
@ -2135,13 +2135,13 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
|
||||
osSyncPrintf("%d本 Item_MaxGet=%d\n", AMMO(ITEM_BOW), CUR_CAPACITY(UPG_QUIVER));
|
||||
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_BOW;
|
||||
} else if (item == ITEM_SLINGSHOT) {
|
||||
Inventory_ChangeUpgrade(UPG_BULLET_BAG, 1);
|
||||
INV_CONTENT(ITEM_SLINGSHOT) = ITEM_SLINGSHOT;
|
||||
AMMO(ITEM_SLINGSHOT) = 30;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_SEEDS) {
|
||||
AMMO(ITEM_SLINGSHOT) += 5;
|
||||
@ -2152,11 +2152,11 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
|
||||
if (!(gSaveContext.itemGetInf[1] & 8)) {
|
||||
gSaveContext.itemGetInf[1] |= 8;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
}
|
||||
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_SEEDS;
|
||||
} else if (item == ITEM_SEEDS_30) {
|
||||
AMMO(ITEM_SLINGSHOT) += 30;
|
||||
@ -2167,15 +2167,15 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
|
||||
if (!(gSaveContext.itemGetInf[1] & 8)) {
|
||||
gSaveContext.itemGetInf[1] |= 8;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
}
|
||||
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_SEEDS;
|
||||
} else if (item == ITEM_OCARINA_FAIRY) {
|
||||
INV_CONTENT(ITEM_OCARINA_FAIRY) = ITEM_OCARINA_FAIRY;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_OCARINA_TIME) {
|
||||
INV_CONTENT(ITEM_OCARINA_TIME) = ITEM_OCARINA_TIME;
|
||||
@ -2208,7 +2208,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
}
|
||||
}
|
||||
}
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_BEAN) {
|
||||
if (gSaveContext.inventory.items[slot] == ITEM_NONE) {
|
||||
@ -2219,25 +2219,25 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
AMMO(ITEM_BEAN)++;
|
||||
BEANS_BOUGHT++;
|
||||
}
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if ((item == ITEM_HEART_PIECE_2) || (item == ITEM_HEART_PIECE)) {
|
||||
gSaveContext.inventory.questItems += 1 << (QUEST_HEART_PIECE + 4);
|
||||
gSaveContext.sohStats.heartPieces++;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_HEART_CONTAINER) {
|
||||
gSaveContext.healthCapacity += 0x10;
|
||||
gSaveContext.health += 0x10;
|
||||
gSaveContext.sohStats.heartContainers++;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_HEART) {
|
||||
osSyncPrintf("回復ハート回復ハート回復ハート\n"); // "Recovery Heart"
|
||||
if (play != NULL) {
|
||||
Health_ChangeBy(play, 0x10);
|
||||
}
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return item;
|
||||
} else if (item == ITEM_MAGIC_SMALL) {
|
||||
if (gSaveContext.magicState != 10) {
|
||||
@ -2252,11 +2252,11 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
|
||||
if (!(gSaveContext.infTable[25] & 0x100)) {
|
||||
gSaveContext.infTable[25] |= 0x100;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
}
|
||||
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return item;
|
||||
} else if (item == ITEM_MAGIC_LARGE) {
|
||||
if (gSaveContext.magicState != 10) {
|
||||
@ -2270,15 +2270,15 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
|
||||
if (!(gSaveContext.infTable[25] & 0x100)) {
|
||||
gSaveContext.infTable[25] |= 0x100;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
}
|
||||
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return item;
|
||||
} else if ((item >= ITEM_RUPEE_GREEN) && (item <= ITEM_INVALID_8)) {
|
||||
Rupees_ChangeBy(sAmmoRefillCounts[item - ITEM_RUPEE_GREEN + 10]);
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
} else if (item == ITEM_BOTTLE) {
|
||||
temp = SLOT(item);
|
||||
@ -2286,7 +2286,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (gSaveContext.inventory.items[temp + i] == ITEM_NONE) {
|
||||
gSaveContext.inventory.items[temp + i] = item;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
}
|
||||
}
|
||||
@ -2318,7 +2318,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
}
|
||||
|
||||
gSaveContext.inventory.items[temp + i] = item;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
}
|
||||
}
|
||||
@ -2326,7 +2326,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (gSaveContext.inventory.items[temp + i] == ITEM_NONE) {
|
||||
gSaveContext.inventory.items[temp + i] = item;
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
}
|
||||
}
|
||||
@ -2354,13 +2354,13 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
} else {
|
||||
gSaveContext.equips.buttonItems[i] = ITEM_NONE;
|
||||
}
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return ITEM_NONE;
|
||||
}
|
||||
|
||||
@ -2368,7 +2368,7 @@ u8 Item_Give(PlayState* play, u8 item) {
|
||||
osSyncPrintf("Item_Register(%d)=%d %d\n", slot, item, temp);
|
||||
INV_CONTENT(item) = item;
|
||||
|
||||
PerformAutosave(play, item);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(item);
|
||||
return temp;
|
||||
}
|
||||
|
||||
@ -2721,60 +2721,6 @@ u8 Item_CheckObtainability(u8 item) {
|
||||
return gSaveContext.inventory.items[slot];
|
||||
}
|
||||
|
||||
// Save when receiving an item, unless it's purchased from a shop
|
||||
void PerformAutosave(PlayState* play, u8 item) {
|
||||
if (CVarGetInteger("gAutosave", 0) && (play != NULL) && (play->sceneNum != SCENE_KENJYANOMA) && (gSaveContext.pendingSale == ITEM_NONE) && (play->sceneNum != SCENE_GANON_DEMO)) {
|
||||
if (CVarGetInteger("gAutosaveAllItems", 0)) {
|
||||
Play_PerformSave(play);
|
||||
} else if (CVarGetInteger("gAutosaveMajorItems", 1)) {
|
||||
switch (item) {
|
||||
case ITEM_STICK:
|
||||
case ITEM_NUT:
|
||||
case ITEM_BOMB:
|
||||
case ITEM_BOW:
|
||||
case ITEM_SEEDS:
|
||||
case ITEM_FISHING_POLE:
|
||||
case ITEM_MAGIC_SMALL:
|
||||
case ITEM_MAGIC_LARGE:
|
||||
case ITEM_INVALID_4:
|
||||
case ITEM_INVALID_5:
|
||||
case ITEM_INVALID_6:
|
||||
case ITEM_INVALID_7:
|
||||
case ITEM_HEART:
|
||||
case ITEM_RUPEE_GREEN:
|
||||
case ITEM_RUPEE_BLUE:
|
||||
case ITEM_RUPEE_RED:
|
||||
case ITEM_RUPEE_PURPLE:
|
||||
case ITEM_RUPEE_GOLD:
|
||||
case ITEM_INVALID_8:
|
||||
case ITEM_STICKS_5:
|
||||
case ITEM_STICKS_10:
|
||||
case ITEM_NUTS_5:
|
||||
case ITEM_NUTS_10:
|
||||
case ITEM_BOMBS_5:
|
||||
case ITEM_BOMBS_10:
|
||||
case ITEM_BOMBS_20:
|
||||
case ITEM_BOMBS_30:
|
||||
case ITEM_ARROWS_SMALL:
|
||||
case ITEM_ARROWS_MEDIUM:
|
||||
case ITEM_ARROWS_LARGE:
|
||||
case ITEM_SEEDS_30:
|
||||
break;
|
||||
case ITEM_BOMBCHU:
|
||||
case ITEM_BOMBCHUS_5:
|
||||
case ITEM_BOMBCHUS_20:
|
||||
if (!CVarGetInteger("gBombchuDrops", 0)) {
|
||||
Play_PerformSave(play);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Play_PerformSave(play);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Inventory_DeleteItem(u16 item, u16 invSlot) {
|
||||
s16 i;
|
||||
|
||||
@ -6300,7 +6246,7 @@ void Interface_Update(PlayState* play) {
|
||||
if (gSaveContext.rupeeAccumulator == 0) {
|
||||
u16 tempSaleItem = gSaveContext.pendingSale;
|
||||
gSaveContext.pendingSale = ITEM_NONE;
|
||||
PerformAutosave(play, tempSaleItem);
|
||||
GameInteractor_ExecuteOnReceiveItemHooks(tempSaleItem);
|
||||
}
|
||||
} else {
|
||||
gSaveContext.rupeeAccumulator = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user