2020-01-15 12:07:48 +00:00
|
|
|
#include "Runtime/MP1/CPlayerVisor.hpp"
|
|
|
|
|
|
|
|
#include "Runtime/CSimplePool.hpp"
|
|
|
|
#include "Runtime/CStateManager.hpp"
|
|
|
|
#include "Runtime/GameGlobalObjects.hpp"
|
|
|
|
#include "Runtime/Camera/CGameCamera.hpp"
|
|
|
|
#include "Runtime/Graphics/CBooRenderer.hpp"
|
|
|
|
#include "Runtime/Graphics/CModel.hpp"
|
|
|
|
#include "Runtime/GuiSys/CCompoundTargetReticle.hpp"
|
|
|
|
#include "Runtime/GuiSys/CTargetingManager.hpp"
|
|
|
|
#include "Runtime/World/CPlayer.hpp"
|
|
|
|
|
2019-09-21 13:07:13 +00:00
|
|
|
#include "TCastTo.hpp" // Generated file, do not modify include path
|
2017-02-12 03:17:18 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
namespace urde::MP1 {
|
|
|
|
|
|
|
|
CPlayerVisor::CPlayerVisor(CStateManager&) : x108_newScanPane(EFilterType::Blend, CGraphics::g_SpareTexture.get()) {
|
|
|
|
x25_24_visorTransitioning = false;
|
|
|
|
x25_25_ = false;
|
|
|
|
xcc_scanFrameCorner = g_SimplePool->GetObj("CMDL_ScanFrameCorner");
|
|
|
|
xd8_scanFrameCenterSide = g_SimplePool->GetObj("CMDL_ScanFrameCenterSide");
|
|
|
|
xe4_scanFrameCenterTop = g_SimplePool->GetObj("CMDL_ScanFrameCenterTop");
|
|
|
|
xf0_scanFrameStretchSide = g_SimplePool->GetObj("CMDL_ScanFrameStretchSide");
|
|
|
|
xfc_scanFrameStretchTop = g_SimplePool->GetObj("CMDL_ScanFrameStretchTop");
|
|
|
|
// x108_newScanPane = g_SimplePool->GetObj("CMDL_NewScanPane");
|
|
|
|
x114_scanShield = g_SimplePool->GetObj("CMDL_ScanShield");
|
|
|
|
x124_scanIconNoncritical = g_SimplePool->GetObj("CMDL_ScanIconNoncritical");
|
|
|
|
x130_scanIconCritical = g_SimplePool->GetObj("CMDL_ScanIconCritical");
|
|
|
|
x13c_scanTargets.resize(64);
|
|
|
|
x540_xrayPalette = g_SimplePool->GetObj("TXTR_XRayPalette");
|
|
|
|
x0_scanWindowSizes.push_back({});
|
|
|
|
x0_scanWindowSizes.push_back({g_tweakGui->GetScanWindowIdleWidth(), g_tweakGui->GetScanWindowIdleHeight()});
|
|
|
|
x0_scanWindowSizes.push_back({g_tweakGui->GetScanWindowActiveWidth(), g_tweakGui->GetScanWindowActiveHeight()});
|
2017-05-29 19:55:44 +00:00
|
|
|
}
|
2017-02-12 03:17:18 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
CPlayerVisor::~CPlayerVisor() {
|
|
|
|
CSfxManager::SfxStop(x5c_visorLoopSfx);
|
|
|
|
CSfxManager::SfxStop(x60_scanningLoopSfx);
|
2017-02-12 03:17:18 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
int CPlayerVisor::FindEmptyInactiveScanTarget() const {
|
2019-06-12 02:05:17 +00:00
|
|
|
for (size_t i = 0; i < x13c_scanTargets.size(); ++i) {
|
2018-12-08 05:30:43 +00:00
|
|
|
const SScanTarget& tgt = x13c_scanTargets[i];
|
|
|
|
if (tgt.x4_timer == 0.f)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return -1;
|
2017-05-29 19:55:44 +00:00
|
|
|
}
|
2017-05-18 19:27:21 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
int CPlayerVisor::FindCachedInactiveScanTarget(TUniqueId uid) const {
|
2019-06-12 02:05:17 +00:00
|
|
|
for (size_t i = 0; i < x13c_scanTargets.size(); ++i) {
|
2018-12-08 05:30:43 +00:00
|
|
|
const SScanTarget& tgt = x13c_scanTargets[i];
|
|
|
|
if (tgt.x0_objId == uid && tgt.x4_timer > 0.f)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return -1;
|
2017-05-18 19:27:21 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
bool CPlayerVisor::DrawScanObjectIndicators(const CStateManager& mgr) const {
|
|
|
|
if (!x124_scanIconNoncritical.IsLoaded() || !x130_scanIconCritical.IsLoaded())
|
|
|
|
return false;
|
|
|
|
if (!x114_scanShield.IsLoaded())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
CGraphics::SetDepthRange(DEPTH_WORLD, DEPTH_FAR);
|
|
|
|
g_Renderer->SetViewportOrtho(true, 0.f, 4096.f);
|
|
|
|
|
|
|
|
float vpScale = g_Viewport.xc_height / 448.f;
|
|
|
|
CGraphics::SetModelMatrix(zeus::CTransform::Scale(x48_interpWindowDims.x() * 17.f * vpScale, 1.f,
|
|
|
|
x48_interpWindowDims.y() * 17.f * vpScale));
|
|
|
|
|
2019-02-24 07:15:54 +00:00
|
|
|
x114_scanShield->Draw(CModelFlags(5, 0, 3, zeus::skClear));
|
2018-12-08 05:30:43 +00:00
|
|
|
|
|
|
|
const CGameCamera* cam = mgr.GetCameraManager()->GetCurrentCamera(mgr);
|
|
|
|
zeus::CTransform camMtx = mgr.GetCameraManager()->GetCurrentCameraTransform(mgr);
|
|
|
|
CGraphics::SetViewPointMatrix(camMtx);
|
|
|
|
zeus::CFrustum frustum;
|
|
|
|
frustum.updatePlanes(camMtx, zeus::CProjection(zeus::SProjPersp(
|
|
|
|
cam->GetFov(), g_Viewport.x8_width / float(g_Viewport.xc_height), 1.f, 100.f)));
|
|
|
|
g_Renderer->SetClippingPlanes(frustum);
|
|
|
|
g_Renderer->SetPerspective(cam->GetFov(), g_Viewport.x8_width, g_Viewport.xc_height, cam->GetNearClipDistance(),
|
|
|
|
cam->GetFarClipDistance());
|
|
|
|
|
|
|
|
for (const SScanTarget& tgt : x13c_scanTargets) {
|
|
|
|
if (tgt.x4_timer == 0.f)
|
|
|
|
continue;
|
|
|
|
if (TCastToConstPtr<CActor> act = mgr.GetObjectById(tgt.x0_objId)) {
|
|
|
|
if (!act->GetMaterialList().HasMaterial(EMaterialTypes::Scannable))
|
|
|
|
continue;
|
|
|
|
const CScannableObjectInfo* scanInfo = act->GetScannableObjectInfo();
|
|
|
|
const CModel* useModel;
|
|
|
|
const zeus::CColor* useColor;
|
|
|
|
const zeus::CColor* useDimColor;
|
|
|
|
if (scanInfo->IsImportant()) {
|
|
|
|
useModel = x130_scanIconCritical.GetObj();
|
|
|
|
useColor = &g_tweakGuiColors->GetScanIconCriticalColor();
|
|
|
|
useDimColor = &g_tweakGuiColors->GetScanIconCriticalDimColor();
|
|
|
|
} else {
|
|
|
|
useModel = x124_scanIconNoncritical.GetObj();
|
|
|
|
useColor = &g_tweakGuiColors->GetScanIconNoncriticalColor();
|
|
|
|
useDimColor = &g_tweakGuiColors->GetScanIconNoncriticalDimColor();
|
|
|
|
}
|
|
|
|
|
|
|
|
zeus::CVector3f scanPos = act->GetScanObjectIndicatorPosition(mgr);
|
|
|
|
float scale = CCompoundTargetReticle::CalculateClampedScale(
|
|
|
|
scanPos, 1.f, g_tweakTargeting->GetScanTargetClampMin(), g_tweakTargeting->GetScanTargetClampMax(), mgr);
|
|
|
|
zeus::CTransform xf(zeus::CMatrix3f(scale) * camMtx.basis, scanPos);
|
|
|
|
|
|
|
|
float scanRange = g_tweakPlayer->GetScanningRange();
|
|
|
|
float farRange = g_tweakPlayer->GetScanMaxLockDistance() - scanRange;
|
|
|
|
float farT;
|
|
|
|
if (farRange <= 0.f)
|
|
|
|
farT = 1.f;
|
|
|
|
else
|
|
|
|
farT = zeus::clamp(0.f, 1.f - ((scanPos - camMtx.origin).magnitude() - scanRange) / farRange, 1.f);
|
|
|
|
|
|
|
|
zeus::CColor iconColor = zeus::CColor::lerp(*useColor, *useDimColor, tgt.x8_inRangeTimer);
|
|
|
|
float iconAlpha;
|
|
|
|
if (mgr.GetPlayerState()->GetScanTime(scanInfo->GetScannableObjectId()) == 1.f) {
|
|
|
|
iconAlpha = tgt.x4_timer * 0.25f;
|
|
|
|
} else {
|
|
|
|
float tmp = 1.f;
|
|
|
|
if (mgr.GetPlayer().GetOrbitTargetId() == tgt.x0_objId)
|
|
|
|
tmp = 0.75f * x2c_scanDimInterp + 0.25f;
|
|
|
|
iconAlpha = tgt.x4_timer * tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
CGraphics::SetModelMatrix(xf);
|
|
|
|
iconColor.a() *= iconAlpha * farT;
|
|
|
|
useModel->Draw(CModelFlags(7, 0, 1, iconColor));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CGraphics::SetDepthRange(DEPTH_SCREEN_ACTORS, DEPTH_GUN);
|
|
|
|
return true;
|
2017-05-21 16:01:04 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CPlayerVisor::UpdateScanObjectIndicators(const CStateManager& mgr, float dt) {
|
|
|
|
bool inBoxExists = false;
|
|
|
|
float dt2 = dt * 2.f;
|
2017-05-30 03:45:31 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
for (SScanTarget& tgt : x13c_scanTargets) {
|
|
|
|
tgt.x4_timer = std::max(0.f, tgt.x4_timer - dt);
|
|
|
|
if (mgr.GetPlayer().ObjectInScanningRange(tgt.x0_objId, mgr))
|
|
|
|
tgt.x8_inRangeTimer = std::max(0.f, tgt.x8_inRangeTimer - dt2);
|
2017-05-30 03:45:31 +00:00
|
|
|
else
|
2018-12-08 05:30:43 +00:00
|
|
|
tgt.x8_inRangeTimer = std::min(1.f, tgt.x8_inRangeTimer + dt2);
|
|
|
|
|
|
|
|
if (TCastToConstPtr<CActor> act = mgr.GetObjectById(tgt.x0_objId)) {
|
|
|
|
const CGameCamera* cam = mgr.GetCameraManager()->GetCurrentCamera(mgr);
|
|
|
|
zeus::CVector3f orbitPos = act->GetOrbitPosition(mgr);
|
|
|
|
orbitPos = cam->ConvertToScreenSpace(orbitPos);
|
|
|
|
orbitPos.x() = orbitPos.x() * g_Viewport.x8_width / 2.f + g_Viewport.x8_width / 2.f;
|
|
|
|
orbitPos.y() = orbitPos.y() * g_Viewport.xc_height / 2.f + g_Viewport.xc_height / 2.f;
|
|
|
|
bool inBox = mgr.GetPlayer().WithinOrbitScreenBox(orbitPos, mgr.GetPlayer().GetOrbitZone(),
|
|
|
|
mgr.GetPlayer().GetOrbitType());
|
|
|
|
if (inBox != tgt.xc_inBox) {
|
|
|
|
tgt.xc_inBox = inBox;
|
|
|
|
if (inBox)
|
|
|
|
x550_scanFrameColorImpulseInterp = 1.f;
|
|
|
|
}
|
|
|
|
inBoxExists |= inBox;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inBoxExists)
|
|
|
|
x54c_scanFrameColorInterp = std::min(x54c_scanFrameColorInterp + dt2, 1.f);
|
|
|
|
else
|
|
|
|
x54c_scanFrameColorInterp = std::max(0.f, x54c_scanFrameColorInterp - dt2);
|
|
|
|
|
|
|
|
x550_scanFrameColorImpulseInterp = std::max(0.f, x550_scanFrameColorImpulseInterp - dt);
|
|
|
|
dt += FLT_EPSILON;
|
|
|
|
|
|
|
|
TAreaId playerArea = mgr.GetPlayer().GetAreaIdAlways();
|
|
|
|
for (TUniqueId id : mgr.GetPlayer().GetNearbyOrbitObjects()) {
|
|
|
|
if (TCastToConstPtr<CActor> act = mgr.GetObjectById(id)) {
|
|
|
|
if (act->GetAreaIdAlways() != playerArea)
|
|
|
|
continue;
|
|
|
|
if (!act->GetMaterialList().HasMaterial(EMaterialTypes::Scannable))
|
|
|
|
continue;
|
|
|
|
int target = FindCachedInactiveScanTarget(id);
|
|
|
|
if (target != -1) {
|
|
|
|
SScanTarget& sTarget = x13c_scanTargets[target];
|
|
|
|
sTarget.x4_timer = std::min(sTarget.x4_timer + dt2, 1.f);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
target = FindEmptyInactiveScanTarget();
|
|
|
|
if (target != -1) {
|
|
|
|
SScanTarget& sTarget = x13c_scanTargets[target];
|
|
|
|
sTarget.x0_objId = id;
|
|
|
|
sTarget.x4_timer = dt;
|
|
|
|
sTarget.x8_inRangeTimer = 1.f;
|
|
|
|
sTarget.xc_inBox = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-05-29 19:55:44 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CPlayerVisor::UpdateScanWindow(float dt, const CStateManager& mgr) {
|
|
|
|
UpdateScanObjectIndicators(mgr, dt);
|
|
|
|
if (mgr.GetPlayer().GetScanningState() == CPlayer::EPlayerScanState::Scanning) {
|
|
|
|
if (!x60_scanningLoopSfx)
|
|
|
|
x60_scanningLoopSfx =
|
|
|
|
CSfxManager::SfxStart(SFXui_scanning_lp, x24_visorSfxVol, 0.f, false, 0x7f, true, kInvalidAreaId);
|
|
|
|
} else {
|
|
|
|
CSfxManager::SfxStop(x60_scanningLoopSfx);
|
|
|
|
x60_scanningLoopSfx.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
EScanWindowState desiredState = GetDesiredScanWindowState(mgr);
|
|
|
|
switch (x34_nextState) {
|
|
|
|
case EScanWindowState::NotInScanVisor:
|
|
|
|
if (desiredState != EScanWindowState::NotInScanVisor) {
|
|
|
|
if (x30_prevState == EScanWindowState::NotInScanVisor)
|
|
|
|
x48_interpWindowDims = x0_scanWindowSizes[int(desiredState)];
|
|
|
|
x50_nextWindowDims = x0_scanWindowSizes[int(desiredState)];
|
|
|
|
x40_prevWindowDims = x48_interpWindowDims;
|
|
|
|
x30_prevState = x34_nextState;
|
|
|
|
x34_nextState = desiredState;
|
|
|
|
x38_windowInterpDuration =
|
|
|
|
(desiredState == EScanWindowState::Scan) ? g_tweakGui->GetScanSidesEndTime() - x3c_windowInterpTimer : 0.f;
|
|
|
|
x3c_windowInterpTimer = x38_windowInterpDuration;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case EScanWindowState::Idle:
|
|
|
|
if (desiredState != EScanWindowState::Idle) {
|
|
|
|
x50_nextWindowDims = (desiredState == EScanWindowState::NotInScanVisor) ? x48_interpWindowDims
|
|
|
|
: x0_scanWindowSizes[int(desiredState)];
|
|
|
|
x40_prevWindowDims = x48_interpWindowDims;
|
|
|
|
x30_prevState = x34_nextState;
|
|
|
|
x34_nextState = desiredState;
|
|
|
|
x38_windowInterpDuration =
|
|
|
|
(desiredState == EScanWindowState::Scan) ? g_tweakGui->GetScanSidesEndTime() - x3c_windowInterpTimer : 0.f;
|
|
|
|
x3c_windowInterpTimer = x38_windowInterpDuration;
|
|
|
|
if (desiredState == EScanWindowState::Scan)
|
|
|
|
CSfxManager::SfxStart(SFXui_into_scan_window, x24_visorSfxVol, 0.f, false, 0x7f, false, kInvalidAreaId);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case EScanWindowState::Scan:
|
|
|
|
if (desiredState != EScanWindowState::Scan) {
|
|
|
|
x50_nextWindowDims = (desiredState == EScanWindowState::NotInScanVisor) ? x48_interpWindowDims
|
|
|
|
: x0_scanWindowSizes[int(desiredState)];
|
|
|
|
x40_prevWindowDims = x48_interpWindowDims;
|
|
|
|
x30_prevState = x34_nextState;
|
|
|
|
x34_nextState = desiredState;
|
|
|
|
x38_windowInterpDuration =
|
|
|
|
(desiredState == EScanWindowState::Idle) ? g_tweakGui->GetScanSidesEndTime() - x3c_windowInterpTimer : 0.f;
|
|
|
|
x3c_windowInterpTimer = x38_windowInterpDuration;
|
|
|
|
if (mgr.GetPlayerState()->GetVisorTransitionFactor() == 1.f)
|
|
|
|
CSfxManager::SfxStart(SFXui_outof_scan_window, x24_visorSfxVol, 0.f, false, 0x7f, false, kInvalidAreaId);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (x30_prevState != x34_nextState) {
|
|
|
|
x3c_windowInterpTimer = std::max(0.f, x3c_windowInterpTimer - dt);
|
|
|
|
if (x3c_windowInterpTimer == 0.f)
|
|
|
|
x30_prevState = x34_nextState;
|
|
|
|
|
|
|
|
float t = 0.f;
|
|
|
|
if (x38_windowInterpDuration > 0.f) {
|
|
|
|
float scanSidesDuration = g_tweakGui->GetScanSidesDuration();
|
|
|
|
float scanSidesStart = g_tweakGui->GetScanSidesStartTime();
|
|
|
|
if (x34_nextState == EScanWindowState::Scan)
|
|
|
|
t = (x3c_windowInterpTimer < scanSidesDuration) ? 0.f
|
|
|
|
: (x3c_windowInterpTimer - scanSidesDuration) / scanSidesStart;
|
|
|
|
else
|
|
|
|
t = (x3c_windowInterpTimer > scanSidesStart) ? 1.f : x3c_windowInterpTimer / scanSidesStart;
|
2017-05-30 03:45:31 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
x48_interpWindowDims = x50_nextWindowDims * (1.f - t) + x40_prevWindowDims * t;
|
|
|
|
}
|
2017-05-29 19:55:44 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
CPlayerVisor::EScanWindowState CPlayerVisor::GetDesiredScanWindowState(const CStateManager& mgr) const {
|
|
|
|
if (mgr.GetPlayerState()->GetCurrentVisor() == CPlayerState::EPlayerVisor::Scan) {
|
|
|
|
switch (mgr.GetPlayer().GetScanningState()) {
|
|
|
|
case CPlayer::EPlayerScanState::Scanning:
|
|
|
|
case CPlayer::EPlayerScanState::ScanComplete:
|
|
|
|
return EScanWindowState::Scan;
|
|
|
|
default:
|
|
|
|
return EScanWindowState::Idle;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return EScanWindowState::NotInScanVisor;
|
2017-05-29 19:55:44 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CPlayerVisor::LockUnlockAssets() {
|
2018-01-07 19:55:09 +00:00
|
|
|
#if 0
|
2017-05-30 03:45:31 +00:00
|
|
|
if (x1c_curVisor == CPlayerState::EPlayerVisor::Scan)
|
|
|
|
x120_assetLockCountdown = 2;
|
|
|
|
else if (x120_assetLockCountdown > 0)
|
|
|
|
--x120_assetLockCountdown;
|
2017-05-29 19:55:44 +00:00
|
|
|
|
2017-05-30 03:45:31 +00:00
|
|
|
if (x120_assetLockCountdown > 0)
|
|
|
|
{
|
|
|
|
xcc_scanFrameCorner.Lock();
|
|
|
|
xd8_scanFrameCenterSide.Lock();
|
|
|
|
xe4_scanFrameCenterTop.Lock();
|
|
|
|
xf0_scanFrameStretchSide.Lock();
|
|
|
|
xfc_scanFrameStretchTop.Lock();
|
2018-01-06 06:50:42 +00:00
|
|
|
//x108_newScanPane.Lock();
|
2017-05-30 03:45:31 +00:00
|
|
|
x114_scanShield.Lock();
|
|
|
|
x124_scanIconNoncritical.Lock();
|
|
|
|
x130_scanIconCritical.Lock();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
xcc_scanFrameCorner.Unlock();
|
|
|
|
xd8_scanFrameCenterSide.Unlock();
|
|
|
|
xe4_scanFrameCenterTop.Unlock();
|
|
|
|
xf0_scanFrameStretchSide.Unlock();
|
|
|
|
xfc_scanFrameStretchTop.Unlock();
|
2018-01-06 06:50:42 +00:00
|
|
|
//x108_newScanPane.Unlock();
|
2017-05-30 03:45:31 +00:00
|
|
|
x114_scanShield.Unlock();
|
|
|
|
x124_scanIconNoncritical.Unlock();
|
|
|
|
x130_scanIconCritical.Unlock();
|
|
|
|
}
|
2018-01-07 19:55:09 +00:00
|
|
|
#endif
|
2017-05-29 19:55:44 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CPlayerVisor::DrawScanEffect(const CStateManager& mgr, const CTargetingManager* tgtMgr) const {
|
2019-07-21 08:42:52 +00:00
|
|
|
SCOPED_GRAPHICS_DEBUG_GROUP("CPlayerVisor::DrawScanEffect", zeus::skMagenta);
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
bool indicatorsDrawn = DrawScanObjectIndicators(mgr);
|
|
|
|
if (tgtMgr && indicatorsDrawn) {
|
|
|
|
CGraphics::SetDepthRange(DEPTH_TARGET_MANAGER, DEPTH_TARGET_MANAGER);
|
|
|
|
tgtMgr->Draw(mgr, false);
|
|
|
|
CGraphics::SetDepthRange(DEPTH_SCREEN_ACTORS, DEPTH_GUN);
|
|
|
|
}
|
|
|
|
|
|
|
|
float transFactor = mgr.GetPlayerState()->GetVisorTransitionFactor();
|
|
|
|
float scanSidesDuration = g_tweakGui->GetScanSidesDuration();
|
|
|
|
float scanSidesStart = g_tweakGui->GetScanSidesStartTime();
|
|
|
|
|
|
|
|
float t;
|
|
|
|
if (x34_nextState == EScanWindowState::Scan)
|
|
|
|
t = 1.f - ((x3c_windowInterpTimer < scanSidesDuration)
|
|
|
|
? 0.f
|
|
|
|
: (x3c_windowInterpTimer - scanSidesDuration) / scanSidesStart);
|
|
|
|
else
|
|
|
|
t = (x3c_windowInterpTimer > scanSidesStart) ? 1.f : x3c_windowInterpTimer / scanSidesStart;
|
|
|
|
|
|
|
|
float vpScale = g_Viewport.xc_height / 448.f;
|
|
|
|
float divisor = (transFactor * ((1.f - t) * x58_scanMagInterp + t * g_tweakGui->GetScanWindowScanningAspect()) +
|
|
|
|
(1.f - transFactor));
|
|
|
|
divisor = 1.f / divisor;
|
|
|
|
float vpW = 169.218f * x48_interpWindowDims.x() * divisor;
|
|
|
|
vpW = zeus::clamp(0.f, vpW, 640.f) * vpScale;
|
|
|
|
float vpH = 152.218f * x48_interpWindowDims.y() * divisor;
|
|
|
|
vpH = zeus::clamp(0.f, vpH, 448.f) * vpScale;
|
|
|
|
|
|
|
|
SClipScreenRect rect;
|
|
|
|
rect.x4_left = (g_Viewport.x8_width - vpW) / 2.f;
|
|
|
|
rect.x8_top = (g_Viewport.xc_height - vpH) / 2.f;
|
|
|
|
rect.xc_width = vpW;
|
|
|
|
rect.x10_height = vpH;
|
|
|
|
CGraphics::ResolveSpareTexture(rect);
|
|
|
|
|
|
|
|
x64_scanDim.Draw();
|
|
|
|
|
|
|
|
g_Renderer->SetViewportOrtho(true, -1.f, 1.f);
|
|
|
|
|
|
|
|
zeus::CTransform windowScale = zeus::CTransform::Scale(x48_interpWindowDims.x(), 1.f, x48_interpWindowDims.y());
|
|
|
|
zeus::CTransform seventeenScale = zeus::CTransform::Scale(17.f * vpScale, 1.f, 17.f * vpScale);
|
|
|
|
CGraphics::SetModelMatrix(seventeenScale * windowScale);
|
|
|
|
|
|
|
|
float uvX0 = rect.x4_left / float(g_Viewport.x8_width);
|
|
|
|
float uvX1 = (rect.x4_left + rect.xc_width) / float(g_Viewport.x8_width);
|
|
|
|
float uvY0 = rect.x8_top / float(g_Viewport.xc_height);
|
|
|
|
float uvY1 = (rect.x8_top + rect.x10_height) / float(g_Viewport.xc_height);
|
|
|
|
CTexturedQuadFilter::Vert rttVerts[4] = {{{-5.f, 0.f, 4.45f}, {uvX0, uvY0}},
|
|
|
|
{{5.f, 0.f, 4.45f}, {uvX1, uvY0}},
|
|
|
|
{{-5.f, 0.f, -4.45f}, {uvX0, uvY1}},
|
|
|
|
{{5.f, 0.f, -4.45f}, {uvX1, uvY1}}};
|
|
|
|
if (CGraphics::g_BooPlatform == boo::IGraphicsDataFactory::Platform::OpenGL) {
|
|
|
|
rttVerts[0].m_uv.y() = uvY1;
|
|
|
|
rttVerts[1].m_uv.y() = uvY1;
|
|
|
|
rttVerts[2].m_uv.y() = uvY0;
|
|
|
|
rttVerts[3].m_uv.y() = uvY0;
|
|
|
|
}
|
|
|
|
const_cast<CTexturedQuadFilter&>(x108_newScanPane).drawVerts(zeus::CColor(1.f, transFactor), rttVerts);
|
|
|
|
|
|
|
|
// No cull faces
|
|
|
|
|
|
|
|
zeus::CColor frameColor = zeus::CColor::lerp(g_tweakGuiColors->GetScanFrameInactiveColor(),
|
|
|
|
g_tweakGuiColors->GetScanFrameActiveColor(), x54c_scanFrameColorInterp);
|
|
|
|
frameColor.a() = transFactor;
|
|
|
|
|
|
|
|
CModelFlags flags(5, 0, 0,
|
|
|
|
frameColor + g_tweakGuiColors->GetScanFrameImpulseColor() *
|
|
|
|
zeus::CColor(x550_scanFrameColorImpulseInterp, x550_scanFrameColorImpulseInterp));
|
|
|
|
flags.m_noCull = true;
|
|
|
|
|
|
|
|
zeus::CTransform verticalFlip = zeus::CTransform::Scale(1.f, 1.f, -1.f);
|
|
|
|
zeus::CTransform horizontalFlip = zeus::CTransform::Scale(-1.f, 1.f, 1.f);
|
|
|
|
|
|
|
|
if (xe4_scanFrameCenterTop.IsLoaded()) {
|
|
|
|
zeus::CTransform modelXf =
|
|
|
|
seventeenScale * zeus::CTransform::Translate(windowScale * zeus::CVector3f(0.f, 0.f, 4.553f));
|
|
|
|
CGraphics::SetModelMatrix(modelXf);
|
|
|
|
xe4_scanFrameCenterTop->Draw(flags);
|
|
|
|
CGraphics::SetModelMatrix(verticalFlip * modelXf);
|
|
|
|
xe4_scanFrameCenterTop->Draw(flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xd8_scanFrameCenterSide.IsLoaded()) {
|
|
|
|
zeus::CTransform modelXf =
|
|
|
|
seventeenScale * zeus::CTransform::Translate(windowScale * zeus::CVector3f(-5.f, 0.f, 0.f));
|
|
|
|
CGraphics::SetModelMatrix(modelXf);
|
|
|
|
xd8_scanFrameCenterSide->Draw(flags);
|
|
|
|
CGraphics::SetModelMatrix(horizontalFlip * modelXf);
|
|
|
|
xd8_scanFrameCenterSide->Draw(flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xcc_scanFrameCorner.IsLoaded()) {
|
|
|
|
zeus::CTransform modelXf =
|
|
|
|
seventeenScale * zeus::CTransform::Translate(windowScale * zeus::CVector3f(-5.f, 0.f, 4.553f));
|
|
|
|
CGraphics::SetModelMatrix(modelXf);
|
|
|
|
xcc_scanFrameCorner->Draw(flags);
|
|
|
|
CGraphics::SetModelMatrix(horizontalFlip * modelXf);
|
|
|
|
xcc_scanFrameCorner->Draw(flags);
|
|
|
|
CGraphics::SetModelMatrix(verticalFlip * modelXf);
|
|
|
|
xcc_scanFrameCorner->Draw(flags);
|
|
|
|
CGraphics::SetModelMatrix(verticalFlip * horizontalFlip * modelXf);
|
|
|
|
xcc_scanFrameCorner->Draw(flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xfc_scanFrameStretchTop.IsLoaded()) {
|
|
|
|
zeus::CTransform modelXf = seventeenScale *
|
|
|
|
zeus::CTransform::Translate(-1.f, 0.f, 4.553f * windowScale.basis[2][2]) *
|
|
|
|
zeus::CTransform::Scale(5.f * windowScale.basis[0][0] - 1.f - 1.884f, 1.f, 1.f);
|
|
|
|
CGraphics::SetModelMatrix(modelXf);
|
|
|
|
xfc_scanFrameStretchTop->Draw(flags);
|
|
|
|
CGraphics::SetModelMatrix(horizontalFlip * modelXf);
|
|
|
|
xfc_scanFrameStretchTop->Draw(flags);
|
|
|
|
CGraphics::SetModelMatrix(verticalFlip * modelXf);
|
|
|
|
xfc_scanFrameStretchTop->Draw(flags);
|
|
|
|
CGraphics::SetModelMatrix(verticalFlip * horizontalFlip * modelXf);
|
|
|
|
xfc_scanFrameStretchTop->Draw(flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xf0_scanFrameStretchSide.IsLoaded()) {
|
|
|
|
zeus::CTransform modelXf = seventeenScale * zeus::CTransform::Translate(-5.f * windowScale.basis[0][0], 0.f, 1.f) *
|
|
|
|
zeus::CTransform::Scale(1.f, 1.f, 4.553f * windowScale.basis[2][2] - 1.f - 1.886f);
|
|
|
|
CGraphics::SetModelMatrix(modelXf);
|
|
|
|
xf0_scanFrameStretchSide->Draw(flags);
|
|
|
|
CGraphics::SetModelMatrix(horizontalFlip * modelXf);
|
|
|
|
xf0_scanFrameStretchSide->Draw(flags);
|
|
|
|
CGraphics::SetModelMatrix(verticalFlip * modelXf);
|
|
|
|
xf0_scanFrameStretchSide->Draw(flags);
|
|
|
|
CGraphics::SetModelMatrix(verticalFlip * horizontalFlip * modelXf);
|
|
|
|
xf0_scanFrameStretchSide->Draw(flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
// cull faces
|
2017-05-29 19:55:44 +00:00
|
|
|
}
|
|
|
|
|
2019-07-21 08:42:52 +00:00
|
|
|
void CPlayerVisor::DrawXRayEffect(const CStateManager&) const {
|
|
|
|
SCOPED_GRAPHICS_DEBUG_GROUP("CPlayerVisor::DrawXRayEffect", zeus::skMagenta);
|
|
|
|
const_cast<CCameraBlurPass&>(x90_xrayBlur).Draw();
|
|
|
|
}
|
2017-05-29 19:55:44 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CPlayerVisor::DrawThermalEffect(const CStateManager&) const {
|
|
|
|
// Empty
|
2017-05-29 19:55:44 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CPlayerVisor::UpdateCurrentVisor(float transFactor) {
|
|
|
|
switch (x1c_curVisor) {
|
|
|
|
case CPlayerState::EPlayerVisor::XRay:
|
|
|
|
x90_xrayBlur.SetBlur(EBlurType::Xray, 36.f * transFactor, 0.f);
|
|
|
|
break;
|
|
|
|
case CPlayerState::EPlayerVisor::Scan: {
|
|
|
|
zeus::CColor dimColor =
|
2019-02-24 07:15:54 +00:00
|
|
|
zeus::CColor::lerp(g_tweakGuiColors->GetScanVisorHudLightMultiply(), zeus::skWhite, 1.f - transFactor);
|
2018-12-08 05:30:43 +00:00
|
|
|
x64_scanDim.SetFilter(EFilterType::Multiply, EFilterShape::Fullscreen, 0.f, dimColor, -1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2017-05-29 19:55:44 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CPlayerVisor::FinishTransitionIn() {
|
|
|
|
switch (x1c_curVisor) {
|
|
|
|
case CPlayerState::EPlayerVisor::Combat:
|
|
|
|
x90_xrayBlur.DisableBlur(0.f);
|
|
|
|
break;
|
|
|
|
case CPlayerState::EPlayerVisor::XRay:
|
|
|
|
x90_xrayBlur.SetBlur(EBlurType::Xray, 36.f, 0.f);
|
|
|
|
if (!x5c_visorLoopSfx)
|
|
|
|
x5c_visorLoopSfx =
|
|
|
|
CSfxManager::SfxStart(SFXui_visor_xray_lp, x24_visorSfxVol, 0.f, false, 0x7f, true, kInvalidAreaId);
|
|
|
|
break;
|
|
|
|
case CPlayerState::EPlayerVisor::Scan: {
|
|
|
|
zeus::CColor dimColor = zeus::CColor::lerp(g_tweakGuiColors->GetScanVisorScreenDimColor(),
|
|
|
|
g_tweakGuiColors->GetScanVisorHudLightMultiply(), x2c_scanDimInterp);
|
|
|
|
x64_scanDim.SetFilter(EFilterType::Multiply, EFilterShape::Fullscreen, 0.f, dimColor, -1);
|
|
|
|
if (!x5c_visorLoopSfx)
|
|
|
|
x5c_visorLoopSfx =
|
|
|
|
CSfxManager::SfxStart(SFXui_visor_scan_lp, x24_visorSfxVol, 0.f, false, 0x7f, true, kInvalidAreaId);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CPlayerState::EPlayerVisor::Thermal:
|
|
|
|
if (!x5c_visorLoopSfx)
|
|
|
|
x5c_visorLoopSfx =
|
|
|
|
CSfxManager::SfxStart(SFXui_visor_thermal_lp, x24_visorSfxVol, 0.f, false, 0x7f, true, kInvalidAreaId);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2017-05-29 19:55:44 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CPlayerVisor::BeginTransitionIn(const CStateManager&) {
|
|
|
|
switch (x1c_curVisor) {
|
|
|
|
case CPlayerState::EPlayerVisor::XRay:
|
|
|
|
x90_xrayBlur.SetBlur(EBlurType::Xray, 0.f, 0.f);
|
|
|
|
// xc4_vpScaleX = 0.9f;
|
|
|
|
// xc8_vpScaleY = 0.9f;
|
|
|
|
CSfxManager::SfxStart(SFXui_into_visor, x24_visorSfxVol, 0.f, false, 0x7f, false, kInvalidAreaId);
|
|
|
|
break;
|
|
|
|
case CPlayerState::EPlayerVisor::Scan:
|
|
|
|
CSfxManager::SfxStart(SFXui_into_visor, x24_visorSfxVol, 0.f, false, 0x7f, false, kInvalidAreaId);
|
2019-02-24 07:15:54 +00:00
|
|
|
x64_scanDim.SetFilter(EFilterType::Multiply, EFilterShape::Fullscreen, 0.f, zeus::skWhite, -1);
|
2018-12-08 05:30:43 +00:00
|
|
|
break;
|
|
|
|
case CPlayerState::EPlayerVisor::Thermal:
|
|
|
|
CSfxManager::SfxStart(SFXui_into_visor, x24_visorSfxVol, 0.f, false, 0x7f, false, kInvalidAreaId);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2017-05-29 19:55:44 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CPlayerVisor::FinishTransitionOut(const CStateManager&) {
|
|
|
|
switch (x1c_curVisor) {
|
|
|
|
case CPlayerState::EPlayerVisor::XRay:
|
|
|
|
x90_xrayBlur.DisableBlur(0.f);
|
|
|
|
// xc4_vpScaleX = 1.f;
|
|
|
|
// xc8_vpScaleY = 1.f;
|
|
|
|
break;
|
|
|
|
case CPlayerState::EPlayerVisor::Scan:
|
|
|
|
x64_scanDim.DisableFilter(0.f);
|
|
|
|
x34_nextState = EScanWindowState::NotInScanVisor;
|
|
|
|
x30_prevState = EScanWindowState::NotInScanVisor;
|
|
|
|
break;
|
|
|
|
case CPlayerState::EPlayerVisor::Thermal:
|
|
|
|
x90_xrayBlur.DisableBlur(0.f);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2017-05-29 19:55:44 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CPlayerVisor::BeginTransitionOut() {
|
|
|
|
if (x5c_visorLoopSfx) {
|
|
|
|
CSfxManager::SfxStop(x5c_visorLoopSfx);
|
|
|
|
x5c_visorLoopSfx.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (x1c_curVisor) {
|
|
|
|
case CPlayerState::EPlayerVisor::XRay:
|
|
|
|
CSfxManager::SfxStart(SFXui_outof_visor, x24_visorSfxVol, 0.f, false, 0x7f, false, kInvalidAreaId);
|
|
|
|
break;
|
|
|
|
case CPlayerState::EPlayerVisor::Scan:
|
|
|
|
if (x60_scanningLoopSfx) {
|
|
|
|
CSfxManager::SfxStop(x60_scanningLoopSfx);
|
|
|
|
x60_scanningLoopSfx.reset();
|
|
|
|
}
|
|
|
|
CSfxManager::SfxStart(SFXui_outof_visor, x24_visorSfxVol, 0.f, false, 0x7f, false, kInvalidAreaId);
|
|
|
|
break;
|
|
|
|
case CPlayerState::EPlayerVisor::Thermal:
|
|
|
|
CSfxManager::SfxStart(SFXui_outof_visor, x24_visorSfxVol, 0.f, false, 0x7f, false, kInvalidAreaId);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2017-05-29 19:55:44 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CPlayerVisor::Update(float dt, const CStateManager& mgr) {
|
|
|
|
x90_xrayBlur.Update(dt);
|
|
|
|
|
|
|
|
CPlayerState& playerState = *mgr.GetPlayerState();
|
|
|
|
CPlayerState::EPlayerVisor activeVisor = playerState.GetActiveVisor(mgr);
|
|
|
|
CPlayerState::EPlayerVisor curVisor = playerState.GetCurrentVisor();
|
|
|
|
CPlayerState::EPlayerVisor transVisor = playerState.GetTransitioningVisor();
|
|
|
|
bool visorTransitioning = playerState.GetIsVisorTransitioning();
|
|
|
|
|
|
|
|
UpdateScanWindow(dt, mgr);
|
|
|
|
|
|
|
|
if (x20_nextVisor != transVisor)
|
|
|
|
x20_nextVisor = transVisor;
|
|
|
|
|
|
|
|
LockUnlockAssets();
|
|
|
|
|
|
|
|
if (mgr.GetPlayer().GetScanningState() == CPlayer::EPlayerScanState::ScanComplete)
|
|
|
|
x2c_scanDimInterp = std::max(0.f, x2c_scanDimInterp - 2.f * dt);
|
|
|
|
else
|
|
|
|
x2c_scanDimInterp = std::min(x2c_scanDimInterp + 2.f * dt, 1.f);
|
|
|
|
|
|
|
|
if (visorTransitioning) {
|
|
|
|
if (!x25_24_visorTransitioning)
|
|
|
|
BeginTransitionOut();
|
|
|
|
if (x1c_curVisor != curVisor) {
|
|
|
|
FinishTransitionOut(mgr);
|
|
|
|
x1c_curVisor = curVisor;
|
|
|
|
BeginTransitionIn(mgr);
|
|
|
|
}
|
|
|
|
UpdateCurrentVisor(playerState.GetVisorTransitionFactor());
|
|
|
|
} else {
|
|
|
|
if (x25_24_visorTransitioning) {
|
|
|
|
FinishTransitionIn();
|
|
|
|
} else if (curVisor == CPlayerState::EPlayerVisor::Scan) {
|
|
|
|
zeus::CColor dimColor = zeus::CColor::lerp(g_tweakGuiColors->GetScanVisorScreenDimColor(),
|
|
|
|
g_tweakGuiColors->GetScanVisorHudLightMultiply(), x2c_scanDimInterp);
|
|
|
|
x64_scanDim.SetFilter(EFilterType::Multiply, EFilterShape::Fullscreen, 0.f, dimColor, -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
x25_24_visorTransitioning = visorTransitioning;
|
|
|
|
|
|
|
|
if (x1c_curVisor != activeVisor) {
|
|
|
|
if (x24_visorSfxVol != 0.f) {
|
|
|
|
x24_visorSfxVol = 0.f;
|
|
|
|
CSfxManager::SfxVolume(x5c_visorLoopSfx, x24_visorSfxVol);
|
|
|
|
CSfxManager::SfxVolume(x60_scanningLoopSfx, x24_visorSfxVol);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (x24_visorSfxVol != 1.f) {
|
|
|
|
x24_visorSfxVol = 1.f;
|
|
|
|
CSfxManager::SfxVolume(x5c_visorLoopSfx, x24_visorSfxVol);
|
|
|
|
CSfxManager::SfxVolume(x60_scanningLoopSfx, x24_visorSfxVol);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
float scanMag = g_tweakGui->GetScanWindowMagnification();
|
|
|
|
if (x58_scanMagInterp < scanMag)
|
|
|
|
x58_scanMagInterp = std::min(x58_scanMagInterp + 2.f * dt, scanMag);
|
|
|
|
else
|
|
|
|
x58_scanMagInterp = std::max(x58_scanMagInterp - 2.f * dt, scanMag);
|
2017-05-29 19:55:44 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CPlayerVisor::Draw(const CStateManager& mgr, const CTargetingManager* tgtManager) const {
|
2019-02-24 07:15:54 +00:00
|
|
|
CGraphics::SetAmbientColor(zeus::skWhite);
|
2018-12-08 05:30:43 +00:00
|
|
|
CGraphics::DisableAllLights();
|
|
|
|
switch (mgr.GetPlayerState()->GetActiveVisor(mgr)) {
|
|
|
|
case CPlayerState::EPlayerVisor::XRay:
|
|
|
|
DrawXRayEffect(mgr);
|
|
|
|
break;
|
|
|
|
case CPlayerState::EPlayerVisor::Thermal:
|
|
|
|
DrawThermalEffect(mgr);
|
|
|
|
break;
|
|
|
|
case CPlayerState::EPlayerVisor::Scan:
|
|
|
|
DrawScanEffect(mgr, tgtManager);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2017-05-29 19:55:44 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CPlayerVisor::Touch() {
|
|
|
|
if (x124_scanIconNoncritical.IsLoaded())
|
|
|
|
x124_scanIconNoncritical->Touch(0);
|
|
|
|
if (x130_scanIconCritical.IsLoaded())
|
|
|
|
x130_scanIconCritical->Touch(0);
|
2017-05-18 19:27:21 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
float CPlayerVisor::GetDesiredViewportScaleX(const CStateManager& mgr) const {
|
|
|
|
return mgr.GetPlayerState()->GetActiveVisor(mgr) == CPlayerState::EPlayerVisor::Combat ? 1.f : xc4_vpScaleX;
|
2017-05-18 19:27:21 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
float CPlayerVisor::GetDesiredViewportScaleY(const CStateManager& mgr) const {
|
|
|
|
return mgr.GetPlayerState()->GetActiveVisor(mgr) == CPlayerState::EPlayerVisor::Combat ? 1.f : xc8_vpScaleY;
|
2017-05-18 19:27:21 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
} // namespace urde::MP1
|