mirror of https://github.com/AxioDL/metaforce.git
Merge branch 'master' of https://github.com/AxioDL/urde
This commit is contained in:
commit
7a5dff47b7
|
@ -110,13 +110,13 @@ CAutoMapper::CAutoMapper(CStateManager& stateMgr)
|
|||
x48_mapIcons.push_back(g_SimplePool->GetObj(SObjectTag{FOURCC('TXTR'), g_tweakPlayerRes->x10_minesBreakFirstTopIcon}));
|
||||
x48_mapIcons.push_back(g_SimplePool->GetObj(SObjectTag{FOURCC('TXTR'), g_tweakPlayerRes->x14_minesBreakFirstBottomIcon}));
|
||||
|
||||
for (int i=0 ; i<9 ; ++i)
|
||||
for (u32 i=0 ; i<9 ; ++i)
|
||||
{
|
||||
x210_lstick.push_back(g_SimplePool->GetObj(SObjectTag{FOURCC('TXTR'), g_tweakPlayerRes->x24_lStick[i]}));
|
||||
x25c_cstick.push_back(g_SimplePool->GetObj(SObjectTag{FOURCC('TXTR'), g_tweakPlayerRes->x4c_cStick[i]}));
|
||||
}
|
||||
|
||||
for (int i=0 ; i<2 ; ++i)
|
||||
for (u32 i=0 ; i<2 ; ++i)
|
||||
{
|
||||
x2a8_ltrigger.push_back(g_SimplePool->GetObj(SObjectTag{FOURCC('TXTR'), g_tweakPlayerRes->x74_lTrigger[i]}));
|
||||
x2bc_rtrigger.push_back(g_SimplePool->GetObj(SObjectTag{FOURCC('TXTR'), g_tweakPlayerRes->x80_rTrigger[i]}));
|
||||
|
@ -348,8 +348,8 @@ bool CAutoMapper::CanLeaveMapScreen(const CStateManager& mgr) const
|
|||
|
||||
void CAutoMapper::SetCurWorldAssetId(CAssetId mlvlId)
|
||||
{
|
||||
int numWorlds = x8_mapu->GetNumMapWorldDatas();
|
||||
for (int i=0 ; i<numWorlds ; ++i)
|
||||
u32 numWorlds = x8_mapu->GetNumMapWorldDatas();
|
||||
for (u32 i=0 ; i<numWorlds ; ++i)
|
||||
if (x8_mapu->GetMapWorldData(i).GetWorldAssetId() == mlvlId)
|
||||
{
|
||||
x9c_worldIdx = i;
|
||||
|
@ -941,13 +941,13 @@ CAutoMapper::FindClosestVisibleWorld(const zeus::CVector3f& point,
|
|||
{
|
||||
float minDist = 29999.f;
|
||||
std::pair<int, int> closestWorld = {xa0_curAreaId, xa0_curAreaId};
|
||||
for (int w=0 ; w<x8_mapu->GetNumMapWorldDatas() ; ++w)
|
||||
for (u32 w=0 ; w<x8_mapu->GetNumMapWorldDatas() ; ++w)
|
||||
{
|
||||
const CMapUniverse::CMapWorldData& mwData = x8_mapu->GetMapWorldData(w);
|
||||
const CMapWorldInfo& mwInfo = *g_GameState->StateForWorld(mwData.GetWorldAssetId()).MapWorldInfo();
|
||||
if (!mwInfo.IsAnythingSet())
|
||||
continue;
|
||||
for (int i=0 ; i<mwData.GetNumMapAreaDatas() ; ++i)
|
||||
for (u32 i=0 ; i<mwData.GetNumMapAreaDatas() ; ++i)
|
||||
{
|
||||
const zeus::CVector3f& mwOrigin = mwData.GetMapAreaData(i).origin;
|
||||
zeus::CVector3f pointToArea = mwOrigin - point;
|
||||
|
@ -1090,7 +1090,7 @@ void CAutoMapper::ProcessControllerInput(const CFinalInput& input, CStateManager
|
|||
}
|
||||
else if (IsInMapperState(EAutoMapperState::MapScreenUniverse))
|
||||
{
|
||||
int oldWldIdx = x9c_worldIdx;
|
||||
u32 oldWldIdx = x9c_worldIdx;
|
||||
if (x1e0_hintSteps.size())
|
||||
{
|
||||
SAutoMapperHintStep& nextStep = x1e0_hintSteps.front();
|
||||
|
@ -1116,7 +1116,7 @@ void CAutoMapper::ProcessControllerInput(const CFinalInput& input, CStateManager
|
|||
if (x9c_worldIdx != oldWldIdx)
|
||||
{
|
||||
CAssetId curMlvl = g_GameState->CurrentWorldAssetId();
|
||||
for (int i=0 ; i<x14_dummyWorlds.size() ; ++i)
|
||||
for (u32 i=0 ; i<x14_dummyWorlds.size() ; ++i)
|
||||
{
|
||||
auto& wld = x14_dummyWorlds[i];
|
||||
const CMapUniverse::CMapWorldData& mwData = x8_mapu->GetMapWorldData(i);
|
||||
|
@ -1570,7 +1570,7 @@ void CAutoMapper::Draw(const CStateManager& mgr, const zeus::CTransform& xf, flo
|
|||
zeus::CTransform universeAreaXf = mwData.GetWorldTransform() * areaXf;
|
||||
float minMag = FLT_MAX;
|
||||
int hexIdx = -1;
|
||||
for (int i=0 ; i<mwData.GetNumMapAreaDatas() ; ++i)
|
||||
for (u32 i=0 ; i<mwData.GetNumMapAreaDatas() ; ++i)
|
||||
{
|
||||
float mag = (universeAreaXf.origin - mwData.GetMapAreaData(i).origin).magnitude();
|
||||
if (mag < minMag)
|
||||
|
@ -1616,7 +1616,7 @@ void CAutoMapper::Draw(const CStateManager& mgr, const zeus::CTransform& xf, flo
|
|||
if (hintBeaconFilters.size() < x1f8_hintLocations.size())
|
||||
{
|
||||
hintBeaconFilters.reserve(x1f8_hintLocations.size());
|
||||
for (int i=hintBeaconFilters.size() ; i<x1f8_hintLocations.size() ; ++i)
|
||||
for (u32 i=hintBeaconFilters.size() ; i<x1f8_hintLocations.size() ; ++i)
|
||||
hintBeaconFilters.emplace_back(EFilterType::Add, x3c_hintBeacon);
|
||||
}
|
||||
auto locIt = x1f8_hintLocations.cbegin();
|
||||
|
@ -1757,7 +1757,7 @@ void CAutoMapper::SetupHintNavigation()
|
|||
}
|
||||
}
|
||||
|
||||
for (int i=0 ; i<hintOpts.GetHintStates().size() ; ++i)
|
||||
for (u32 i=0 ; i<hintOpts.GetHintStates().size() ; ++i)
|
||||
{
|
||||
const CHintOptions::SHintState& state = hintOpts.GetHintStates()[i];
|
||||
if (navigating && hintOpts.GetNextHintIdx() == i)
|
||||
|
@ -1773,7 +1773,7 @@ void CAutoMapper::SetupHintNavigation()
|
|||
CAssetId CAutoMapper::GetAreaHintDescriptionString(CAssetId mreaId)
|
||||
{
|
||||
const CHintOptions& hintOpts = g_GameState->HintOptions();
|
||||
for (int i=0 ; i<hintOpts.GetHintStates().size() ; ++i)
|
||||
for (u32 i=0 ; i<hintOpts.GetHintStates().size() ; ++i)
|
||||
{
|
||||
const CHintOptions::SHintState& state = hintOpts.GetHintStates()[i];
|
||||
if (state.x0_state != CHintOptions::EHintState::Displaying)
|
||||
|
|
|
@ -417,9 +417,8 @@ void CStateManager::MurderScriptInstanceNames()
|
|||
std::string CStateManager::HashInstanceName(CInputStream& in)
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
static std::string name;
|
||||
while (in.readByte() != 0) {};
|
||||
return name;
|
||||
return "";
|
||||
#else
|
||||
return in.readString();
|
||||
#endif
|
||||
|
|
|
@ -1,21 +1,28 @@
|
|||
#include "CPathCamera.hpp"
|
||||
#include "CCameraManager.hpp"
|
||||
#include "TCastTo.hpp"
|
||||
|
||||
namespace urde
|
||||
{
|
||||
|
||||
CPathCamera::CPathCamera(TUniqueId uid, std::string_view name, const CEntityInfo& info,
|
||||
const zeus::CTransform& xf, bool active, bool, bool, bool, bool,
|
||||
float, float, float, float, float, float, float, u32,
|
||||
EInitialSplinePosition)
|
||||
const zeus::CTransform& xf, bool active, float f1, float f2,
|
||||
float f3, float f4, float f5, u32 flags, EInitialSplinePosition initPos)
|
||||
: CGameCamera(uid, active, name, info, xf,
|
||||
CCameraManager::ThirdPersonFOV(),
|
||||
CCameraManager::NearPlane(),
|
||||
CCameraManager::FarPlane(),
|
||||
CCameraManager::Aspect(), kInvalidUniqueId, 0, 0)
|
||||
, x188_spline(flags & 1), x1dc_(f1), x1e0_(f2), x1e4_(f3), x1e8_initPos(initPos)
|
||||
, x1ec_flags(flags), x1f0_(f4), x1f4_(f5)
|
||||
{
|
||||
}
|
||||
|
||||
void CPathCamera::Accept(IVisitor& visitor)
|
||||
{
|
||||
visitor.Visit(this);
|
||||
}
|
||||
|
||||
void CPathCamera::ProcessInput(const CFinalInput&, CStateManager& mgr)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define __URDE_CPATHCAMERA_HPP__
|
||||
|
||||
#include "CGameCamera.hpp"
|
||||
#include "CCameraSpline.hpp"
|
||||
|
||||
namespace urde
|
||||
{
|
||||
|
@ -12,11 +13,25 @@ public:
|
|||
enum class EInitialSplinePosition
|
||||
{
|
||||
};
|
||||
CPathCamera(TUniqueId, std::string_view name, const CEntityInfo& info,
|
||||
const zeus::CTransform& xf, bool, bool, bool, bool, bool,
|
||||
float, float, float, float, float, float, float, u32,
|
||||
EInitialSplinePosition);
|
||||
private:
|
||||
|
||||
CCameraSpline x188_spline;
|
||||
float x1d4_ = 0.f;
|
||||
float x1d8_ = 0.f;
|
||||
float x1dc_;
|
||||
float x1e0_;
|
||||
float x1e4_;
|
||||
EInitialSplinePosition x1e8_initPos;
|
||||
u32 x1ec_flags;
|
||||
float x1f0_;
|
||||
float x1f4_;
|
||||
public:
|
||||
|
||||
CPathCamera(TUniqueId, std::string_view name, const CEntityInfo& info,
|
||||
const zeus::CTransform& xf, bool, float, float, float,
|
||||
float, float, u32, EInitialSplinePosition);
|
||||
|
||||
void Accept(IVisitor&);
|
||||
void ProcessInput(const CFinalInput&, CStateManager& mgr);
|
||||
void Reset(const zeus::CTransform&, CStateManager& mgr);
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@ void CMapSurfaceShader::draw(const zeus::CColor& color, u32 start, u32 count)
|
|||
Uniform uniform = {
|
||||
CGraphics::GetPerspectiveProjectionMatrix(true) * CGraphics::g_GXModelView.toMatrix4f(), color
|
||||
};
|
||||
m_uniBuf->load(&uniform, sizeof(uniform));
|
||||
m_uniBuf->load(&uniform, sizeof(Uniform));
|
||||
CGraphics::SetShaderDataBinding(m_dataBind);
|
||||
CGraphics::DrawArrayIndexed(start, count);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
#include "CAutoSave.hpp"
|
||||
#include "CSaveGameScreen.hpp"
|
||||
#include "IMain.hpp"
|
||||
|
||||
namespace urde
|
||||
{
|
||||
namespace MP1
|
||||
{
|
||||
CAutoSave::CAutoSave()
|
||||
: CIOWin("")
|
||||
, x14_savegameScreen(new CSaveGameScreen(ESaveContext::InGame, g_GameState->GetCardSerial()))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef __URDE_CAUTOSAVE_HPP__
|
||||
#define __URDE_CAUTOSAVE_HPP__
|
||||
|
||||
#include "CIOWin.hpp"
|
||||
|
||||
namespace urde
|
||||
{
|
||||
namespace MP1
|
||||
{
|
||||
class CSaveGameScreen;
|
||||
class CAutoSave : CIOWin
|
||||
{
|
||||
std::unique_ptr<CSaveGameScreen> x14_savegameScreen;
|
||||
public:
|
||||
CAutoSave();
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif // __URDE_CAUTOSAVE_HPP__
|
|
@ -593,10 +593,11 @@ void CInGameGuiManager::Draw(CStateManager& stateMgr)
|
|||
|
||||
bool notInCine = !stateMgr.GetCameraManager()->IsInCinematicCamera();
|
||||
bool drawVisor = false;
|
||||
if (notInCine && (x1bc_prevState == EInGameGuiState::InGame || x1c0_nextState == EInGameGuiState::InGame))
|
||||
/* Let's always draw the HUD except in cinematic mode */
|
||||
if (notInCine /* && (x1bc_prevState == EInGameGuiState::InGame || x1c0_nextState == EInGameGuiState::InGame) */)
|
||||
drawVisor = true;
|
||||
|
||||
if (x3c_pauseScreenBlur->IsGameDraw())
|
||||
//if (x3c_pauseScreenBlur->IsGameDraw())
|
||||
{
|
||||
x34_samusHud->GetTargetingManager().Draw(stateMgr, true);
|
||||
CGraphics::SetDepthRange(0.015625f, 0.03125f);
|
||||
|
|
|
@ -303,7 +303,8 @@ bool CPauseScreen::ShouldSwitchToInGame() const
|
|||
|
||||
float CPauseScreen::GetHelmetCamYOff() const
|
||||
{
|
||||
if (CPauseScreenBase* screen = x7c_screens[x78_activeIdx].get())
|
||||
CPauseScreenBase* screen = x7c_screens[x78_activeIdx].get();
|
||||
if (screen)
|
||||
return screen->GetCameraYBias();
|
||||
return 0.f;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ set(MP1_WORLD_SOURCES
|
|||
CBeetle.hpp CBeetle.cpp
|
||||
CWarWasp.hpp CWarWasp.cpp
|
||||
CSpacePirate.hpp CSpacePirate.cpp
|
||||
CParasite.hpp CParasite.cpp
|
||||
CBabygoth.hpp CBabygoth.cpp
|
||||
CMetroidPrimeRelay.hpp CMetroidPrimeRelay.cpp
|
||||
CMetroidPrimeExo.hpp CMetroidPrimeExo.cpp
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
#include "CParasite.hpp"
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef __URDE_MP1_CPARASITE_HPP__
|
||||
#define __URDE_MP1_CPARASITE_HPP__
|
||||
|
||||
#include "World/CWallWalker.hpp"
|
||||
|
||||
namespace urde::MP1
|
||||
{
|
||||
class CParasite : public CWallWalker
|
||||
{
|
||||
public:
|
||||
CParasite();
|
||||
};
|
||||
}
|
||||
#endif // __URDE_MP1_CPARASITE_HPP__
|
|
@ -76,6 +76,7 @@ set(WORLD_SOURCES
|
|||
CScriptTargetingPoint.hpp CScriptTargetingPoint.cpp
|
||||
CScriptPlayerActor.hpp CScriptPlayerActor.cpp
|
||||
CScriptSwitch.hpp CScriptSwitch.cpp
|
||||
CWallWalker.hpp CWallWalker.cpp
|
||||
CWallCrawlerSwarm.hpp CWallCrawlerSwarm.cpp
|
||||
CScriptAiJumpPoint.hpp CScriptAiJumpPoint.cpp
|
||||
CScriptRoomAcoustics.hpp CScriptRoomAcoustics.cpp
|
||||
|
|
|
@ -30,7 +30,7 @@ void CScriptAiJumpPoint::Think(float dt, CStateManager&)
|
|||
|
||||
void CScriptAiJumpPoint::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId other, CStateManager& mgr)
|
||||
{
|
||||
AcceptScriptMsg(msg, other, mgr);
|
||||
CActor::AcceptScriptMsg(msg, other, mgr);
|
||||
|
||||
if (msg != EScriptObjectMessage::InitializedInArea)
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#include "CWallWalker.hpp"
|
||||
|
||||
namespace urde
|
||||
{
|
||||
CWallWalker::CWallWalker(ECharacter chr, TUniqueId uid, std::string_view name, EFlavorType flavType,
|
||||
const CEntityInfo& eInfo, const zeus::CTransform& xf,
|
||||
CModelData&& mData, const CPatternedInfo& pInfo, EMovementType mType,
|
||||
EColliderType colType, EBodyType bType, const CActorParameters& aParms, s32 w1, bool w2)
|
||||
: CPatterned(chr, uid, name, flavType, eInfo, xf, std::move(mData), pInfo, mType, colType, bType, aParms, w1)
|
||||
{}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef __URDE_CWALLWALKER_HPP__
|
||||
#define __URDE_CWALLWALKER_HPP__
|
||||
|
||||
#include "CPatterned.hpp"
|
||||
|
||||
namespace urde
|
||||
{
|
||||
class CWallWalker : public CPatterned
|
||||
{
|
||||
public:
|
||||
CWallWalker(ECharacter, TUniqueId, std::string_view, EFlavorType, const CEntityInfo&, const zeus::CTransform&,
|
||||
CModelData&&, const CPatternedInfo&, EMovementType, EColliderType, EBodyType,
|
||||
const CActorParameters&, s32, bool);
|
||||
};
|
||||
}
|
||||
#endif // __URDE_CWALLWALKER_HPP__
|
|
@ -75,8 +75,9 @@
|
|||
#include "MP1/World/CNewIntroBoss.hpp"
|
||||
#include "MP1/World/CSpacePirate.hpp"
|
||||
#include "MP1/World/CWarWasp.hpp"
|
||||
#include "MP1/World/CParasite.hpp"
|
||||
#include "Particle/CWeaponDescription.hpp"
|
||||
|
||||
#include "Camera/CPathCamera.hpp"
|
||||
namespace urde
|
||||
{
|
||||
static logvisor::Module Log("urde::ScriptLoader");
|
||||
|
@ -573,7 +574,7 @@ CEntity* ScriptLoader::LoadEffect(CStateManager& mgr, CInputStream& in, int prop
|
|||
bool b3 = in.readBool();
|
||||
bool b4 = in.readBool();
|
||||
|
||||
if (partId == 0xffffffff && elscId == 0xffffffff)
|
||||
if (!partId.IsValid() && !elscId.IsValid())
|
||||
return nullptr;
|
||||
|
||||
if (!g_ResFactory->GetResourceTypeById(partId) && !g_ResFactory->GetResourceTypeById(elscId))
|
||||
|
@ -840,8 +841,11 @@ CEntity* ScriptLoader::LoadSpawnPoint(CStateManager& mgr, CInputStream& in, int
|
|||
|
||||
CEntity* ScriptLoader::LoadCameraHint(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info)
|
||||
{
|
||||
if (!EnsurePropertyCount(propCount, 25, "CamerHint"))
|
||||
if (propCount > 25)
|
||||
{
|
||||
Log.report(logvisor::Warning, "Too many props (%d > 25) for CameraHint entity", propCount);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SActorHead head = LoadActorHead(in, mgr);
|
||||
|
||||
|
@ -1423,7 +1427,22 @@ CEntity* ScriptLoader::LoadFlickerBat(CStateManager& mgr, CInputStream& in, int
|
|||
|
||||
CEntity* ScriptLoader::LoadPathCamera(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info)
|
||||
{
|
||||
return nullptr;
|
||||
if (propCount > 15)
|
||||
{
|
||||
Log.report(logvisor::Warning, "Too many props (%d > 15) for PathCamera entity", propCount);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SActorHead aHead = LoadActorHead(in, mgr);
|
||||
bool active = in.readBool();
|
||||
u32 flags = LoadParameterFlags(in);
|
||||
float f1 = in.readFloatBig();
|
||||
float f2 = in.readFloatBig();
|
||||
float f3 = in.readFloatBig();
|
||||
CPathCamera::EInitialSplinePosition initPos = CPathCamera::EInitialSplinePosition(in.readUint32Big());
|
||||
float f4 = in.readFloatBig();
|
||||
float f5 = in.readFloatBig();
|
||||
return new CPathCamera(mgr.AllocateUniqueId(), aHead.x0_name, info, aHead.x10_transform, active, f1, f2, f3, f4, f5, flags, initPos);
|
||||
}
|
||||
|
||||
CEntity* ScriptLoader::LoadGrapplePoint(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info)
|
||||
|
@ -1573,7 +1592,44 @@ CEntity* ScriptLoader::LoadSpankWeed(CStateManager& mgr, CInputStream& in, int p
|
|||
|
||||
CEntity* ScriptLoader::LoadParasite(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info)
|
||||
{
|
||||
return nullptr;
|
||||
if (!EnsurePropertyCount(propCount, 6, "Parasite"))
|
||||
return nullptr;
|
||||
|
||||
std::string name = mgr.HashInstanceName(in);
|
||||
CPatterned::EFlavorType flavor = CPatterned::EFlavorType(in.readUint32Big());
|
||||
zeus::CTransform xf = LoadEditorTransform(in);
|
||||
zeus::CVector3f scale = zeus::CVector3f::ReadBig(in);
|
||||
|
||||
std::pair<bool, u32> pcount = CPatternedInfo::HasCorrectParameterCount(in);
|
||||
if (!pcount.first)
|
||||
return nullptr;
|
||||
|
||||
CPatternedInfo pInfo(in, pcount.second);
|
||||
CActorParameters aParms = LoadActorParameters(in);
|
||||
float f1 = in.readFloatBig();
|
||||
float f2 = in.readFloatBig();
|
||||
float f3 = in.readFloatBig();
|
||||
float f4 = in.readFloatBig();
|
||||
float f5 = in.readFloatBig();
|
||||
float f6 = in.readFloatBig();
|
||||
float f7 = in.readFloatBig();
|
||||
float f8 = in.readFloatBig();
|
||||
float f9 = in.readFloatBig();
|
||||
float f10 = in.readFloatBig();
|
||||
float f11 = in.readFloatBig();
|
||||
float f12 = in.readFloatBig();
|
||||
float f13 = in.readFloatBig();
|
||||
float f14 = in.readFloatBig();
|
||||
float f15 = in.readFloatBig();
|
||||
float f16 = in.readFloatBig();
|
||||
float f17 = in.readFloatBig();
|
||||
bool b1 = in.readBool();
|
||||
|
||||
if (g_ResFactory->GetResourceTypeById(pInfo.GetAnimationParameters().GetACSFile()) != SBIG('ANCS'))
|
||||
return nullptr;
|
||||
const CAnimationParameters& animParms = pInfo.GetAnimationParameters();
|
||||
CModelData mData(CAnimRes(animParms.GetACSFile(), animParms.GetCharacter(), scale, animParms.GetInitialAnimation(), true));
|
||||
return nullptr; //return new MP1::CParasite(mgr.AllocateUniqueId(), name, flavor, info, xf, std::move(mData), pInfo, )
|
||||
}
|
||||
|
||||
CEntity* ScriptLoader::LoadPlayerHint(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info)
|
||||
|
|
Loading…
Reference in New Issue