mirror of
https://github.com/thejerf/baijiSteamVR.git
synced 2026-10-19 10:01:43 +02:00
1169 lines
39 KiB
C++
1169 lines
39 KiB
C++
// Copyright 2026 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include "VideoCommon/MetroidElementClassifier.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cmath>
|
|
#include <cctype>
|
|
#include <cstdlib>
|
|
#include <string>
|
|
|
|
namespace
|
|
{
|
|
struct ProfileName
|
|
{
|
|
MetroidElementProfile profile;
|
|
std::string_view ini_name;
|
|
std::string_view display_name;
|
|
};
|
|
|
|
constexpr std::array PROFILE_NAMES{
|
|
ProfileName{MetroidElementProfile::Prime1GC, "metroid_prime_1_gc", "Metroid Prime 1 GC"},
|
|
ProfileName{MetroidElementProfile::Prime1Wii, "metroid_prime_1_wii", "Metroid Prime 1 Wii"},
|
|
ProfileName{MetroidElementProfile::Prime2GC, "metroid_prime_2_gc", "Metroid Prime 2 GC"},
|
|
ProfileName{MetroidElementProfile::Prime2Wii, "metroid_prime_2_wii", "Metroid Prime 2 Wii"},
|
|
ProfileName{MetroidElementProfile::Prime3, "metroid_prime_3", "Metroid Prime 3"},
|
|
ProfileName{MetroidElementProfile::TrilogyAuto, "metroid_prime_trilogy_auto",
|
|
"Metroid Prime Trilogy Auto"},
|
|
};
|
|
|
|
struct LayerName
|
|
{
|
|
MetroidElementLayer layer;
|
|
std::string_view ini_name;
|
|
std::string_view display_name;
|
|
};
|
|
|
|
constexpr std::array LAYER_NAMES{
|
|
LayerName{MetroidElementLayer::Unknown, "METROID_UNKNOWN", "Unknown"},
|
|
LayerName{MetroidElementLayer::Background3D, "METROID_BACKGROUND_3D", "Background 3D"},
|
|
LayerName{MetroidElementLayer::Shadows, "METROID_SHADOWS", "Shadows"},
|
|
LayerName{MetroidElementLayer::Shadow2D, "METROID_SHADOW_2D", "Shadow 2D"},
|
|
LayerName{MetroidElementLayer::BodyShadows, "METROID_BODY_SHADOWS", "Body Shadows"},
|
|
LayerName{MetroidElementLayer::ScreenOverlay, "METROID_SCREEN_OVERLAY", "Screen Overlay"},
|
|
LayerName{MetroidElementLayer::WiiWorld, "METROID_WII_WORLD", "Wii World"},
|
|
LayerName{MetroidElementLayer::World, "METROID_WORLD", "World"},
|
|
LayerName{MetroidElementLayer::XRayWorld, "METROID_XRAY_WORLD", "X-Ray World"},
|
|
LayerName{MetroidElementLayer::XRayEffect, "METROID_XRAY_EFFECT", "X-Ray Effect"},
|
|
LayerName{MetroidElementLayer::ThermalEffect, "METROID_THERMAL_EFFECT", "Thermal Effect"},
|
|
LayerName{MetroidElementLayer::ThermalEffectGun, "METROID_THERMAL_EFFECT_GUN",
|
|
"Thermal Effect Gun"},
|
|
LayerName{MetroidElementLayer::ChargeBeamEffect, "METROID_CHARGE_BEAM_EFFECT",
|
|
"Charge Beam Effect"},
|
|
LayerName{MetroidElementLayer::Gun, "METROID_GUN", "Gun"},
|
|
LayerName{MetroidElementLayer::CinematicWorld, "METROID_CINEMATIC_WORLD", "Cinematic World"},
|
|
LayerName{MetroidElementLayer::EFBCopy, "METROID_EFB_COPY", "EFB Copy"},
|
|
LayerName{MetroidElementLayer::BlackBars, "METROID_BLACK_BARS", "Black Bars"},
|
|
LayerName{MetroidElementLayer::ScreenFade, "METROID_SCREEN_FADE", "Screen Fade"},
|
|
LayerName{MetroidElementLayer::EchoEffect, "METROID_ECHO_EFFECT", "Echo Effect"},
|
|
LayerName{MetroidElementLayer::DarkCentral, "METROID_DARK_CENTRAL", "Dark Central"},
|
|
LayerName{MetroidElementLayer::DarkEffect, "METROID_DARK_EFFECT", "Dark Effect"},
|
|
LayerName{MetroidElementLayer::VisorDirt, "METROID_VISOR_DIRT", "Visor Dirt"},
|
|
LayerName{MetroidElementLayer::ScanHighlighter, "METROID_SCAN_HIGHLIGHTER",
|
|
"Scan Highlighter"},
|
|
LayerName{MetroidElementLayer::ScanBox, "METROID_SCAN_BOX", "Scan Box"},
|
|
LayerName{MetroidElementLayer::ThermalGunAndDoor, "METROID_THERMAL_GUN_AND_DOOR",
|
|
"Thermal Gun and Door"},
|
|
LayerName{MetroidElementLayer::ScanReticle, "METROID_SCAN_RETICLE", "Scan Reticle"},
|
|
LayerName{MetroidElementLayer::Reticle, "METROID_RETICLE", "Reticle"},
|
|
LayerName{MetroidElementLayer::WiiReticle, "METROID_WII_RETICLE", "Wii Reticle"},
|
|
LayerName{MetroidElementLayer::ScanIcons, "METROID_SCAN_ICONS", "Scan Icons"},
|
|
LayerName{MetroidElementLayer::ScanCross, "METROID_SCAN_CROSS", "Scan Cross"},
|
|
LayerName{MetroidElementLayer::MorphballWorld, "METROID_MORPHBALL_WORLD",
|
|
"Morphball World"},
|
|
LayerName{MetroidElementLayer::UnknownWorld, "METROID_UNKNOWN_WORLD", "Unknown World"},
|
|
LayerName{MetroidElementLayer::ScanCircle, "METROID_SCAN_CIRCLE", "Scan Circle"},
|
|
LayerName{MetroidElementLayer::ScanDarken, "METROID_SCAN_DARKEN", "Scan Darken"},
|
|
LayerName{MetroidElementLayer::Helmet, "METROID_HELMET", "Helmet"},
|
|
LayerName{MetroidElementLayer::HUD, "METROID_HUD", "HUD"},
|
|
LayerName{MetroidElementLayer::MorphballHUD, "METROID_MORPHBALL_HUD", "Morphball HUD"},
|
|
LayerName{MetroidElementLayer::XRayHUD, "METROID_XRAY_HUD", "X-Ray HUD"},
|
|
LayerName{MetroidElementLayer::DarkVisorHUD, "METROID_DARK_VISOR_HUD", "Dark Visor HUD"},
|
|
LayerName{MetroidElementLayer::VisorRadarHint, "METROID_VISOR_RADAR_HINT",
|
|
"Visor Radar Hint"},
|
|
LayerName{MetroidElementLayer::RadarDot, "METROID_RADAR_DOT", "Radar Dot"},
|
|
LayerName{MetroidElementLayer::MorphballMapOrHint, "METROID_MORPHBALL_MAP_OR_HINT",
|
|
"Morphball Map or Hint"},
|
|
LayerName{MetroidElementLayer::MapOrHint, "METROID_MAP_OR_HINT", "Map or Hint"},
|
|
LayerName{MetroidElementLayer::MorphballMap, "METROID_MORPHBALL_MAP", "Morphball Map"},
|
|
LayerName{MetroidElementLayer::Map, "METROID_MAP", "Map"},
|
|
LayerName{MetroidElementLayer::Map0, "METROID_MAP_0", "Map 0"},
|
|
LayerName{MetroidElementLayer::Map1, "METROID_MAP_1", "Map 1"},
|
|
LayerName{MetroidElementLayer::Map2, "METROID_MAP_2", "Map 2"},
|
|
LayerName{MetroidElementLayer::Dialog, "METROID_DIALOG", "Dialog"},
|
|
LayerName{MetroidElementLayer::MapMap, "METROID_MAP_MAP", "Map Screen Map"},
|
|
LayerName{MetroidElementLayer::MapLegend, "METROID_MAP_LEGEND", "Map Legend"},
|
|
LayerName{MetroidElementLayer::InventorySamus, "METROID_INVENTORY_SAMUS",
|
|
"Inventory Samus"},
|
|
LayerName{MetroidElementLayer::InventorySamusOutline, "METROID_INVENTORY_SAMUS_OUTLINE",
|
|
"Inventory Samus Outline"},
|
|
LayerName{MetroidElementLayer::MapNorth, "METROID_MAP_NORTH", "Map North"},
|
|
LayerName{MetroidElementLayer::ScanVisor, "METROID_SCAN_VISOR", "Scan Visor"},
|
|
LayerName{MetroidElementLayer::ScanText, "METROID_SCAN_TEXT", "Scan Text"},
|
|
LayerName{MetroidElementLayer::ScanHologram, "METROID_SCAN_HOLOGRAM", "Scan Hologram"},
|
|
LayerName{MetroidElementLayer::Visor, "METROID_VISOR", "Visor"},
|
|
LayerName{MetroidElementLayer::VisorBootup, "METROID_VISOR_BOOTUP", "Visor Bootup"},
|
|
LayerName{MetroidElementLayer::UnknownVisor, "METROID_UNKNOWN_VISOR", "Unknown Visor"},
|
|
LayerName{MetroidElementLayer::UnknownHUD, "METROID_UNKNOWN_HUD", "Unknown HUD"},
|
|
LayerName{MetroidElementLayer::MousePointer, "METROID_MOUSE_POINTER", "Mouse Pointer"},
|
|
LayerName{MetroidElementLayer::Unknown2D, "METROID_UNKNOWN_2D", "Unknown 2D"},
|
|
};
|
|
|
|
std::string NormalizeKey(std::string_view value)
|
|
{
|
|
std::string normalized;
|
|
normalized.reserve(value.size());
|
|
for (const char c : value)
|
|
{
|
|
const auto uc = static_cast<unsigned char>(c);
|
|
if (std::isalnum(uc))
|
|
normalized.push_back(static_cast<char>(std::toupper(uc)));
|
|
}
|
|
return normalized;
|
|
}
|
|
|
|
int Round100(float x)
|
|
{
|
|
return static_cast<int>(std::floor(x * 100.0f + 0.5f));
|
|
}
|
|
|
|
bool Near(int value, int target, int tolerance)
|
|
{
|
|
return std::abs(value - target) <= tolerance;
|
|
}
|
|
|
|
bool Far4096(const MetroidElementClassifier::RoundedMetrics& m)
|
|
{
|
|
return Near(m.f, 409600, 1400);
|
|
}
|
|
|
|
bool Far750(const MetroidElementClassifier::RoundedMetrics& m)
|
|
{
|
|
return Near(m.f, 75000, 800);
|
|
}
|
|
|
|
bool HudV(const MetroidElementClassifier::RoundedMetrics& m)
|
|
{
|
|
return Near(m.v, 6500, 700);
|
|
}
|
|
|
|
bool CompactHudProjection(const MetroidElementClassifier::RoundedMetrics& m)
|
|
{
|
|
return Far4096(m) && m.h >= 7800 && m.h <= 8400 && m.v >= 5600 && m.v <= 6800;
|
|
}
|
|
|
|
bool Prime1XRayEffectProjection(const MetroidElementClassifier::RoundedMetrics& m)
|
|
{
|
|
return m.l == 0 &&
|
|
((Near(m.n, -100, 20) && Near(m.f, 100, 20)) ||
|
|
(Near(m.r, 6400, 80) && Near(m.t, 4400, 80) && m.b == 0));
|
|
}
|
|
|
|
bool IsWorldLayer(MetroidElementLayer layer)
|
|
{
|
|
switch (layer)
|
|
{
|
|
case MetroidElementLayer::Unknown:
|
|
case MetroidElementLayer::Background3D:
|
|
case MetroidElementLayer::Shadows:
|
|
case MetroidElementLayer::Shadow2D:
|
|
case MetroidElementLayer::BodyShadows:
|
|
case MetroidElementLayer::WiiWorld:
|
|
case MetroidElementLayer::World:
|
|
case MetroidElementLayer::XRayWorld:
|
|
case MetroidElementLayer::Gun:
|
|
case MetroidElementLayer::CinematicWorld:
|
|
case MetroidElementLayer::Reticle:
|
|
case MetroidElementLayer::WiiReticle:
|
|
case MetroidElementLayer::ScanIcons:
|
|
case MetroidElementLayer::ScanCross:
|
|
case MetroidElementLayer::MorphballWorld:
|
|
case MetroidElementLayer::UnknownWorld:
|
|
case MetroidElementLayer::MousePointer:
|
|
case MetroidElementLayer::Unknown2D:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
MetroidElementClassifier::RoundedMetrics RoundMetrics(const MetroidProjectionMetrics& metrics)
|
|
{
|
|
MetroidElementClassifier::RoundedMetrics rounded;
|
|
rounded.perspective = metrics.perspective;
|
|
rounded.layer = static_cast<int>(metrics.projection_sequence);
|
|
|
|
if (metrics.perspective)
|
|
{
|
|
rounded.h = Round100(metrics.hfov);
|
|
rounded.v = Round100(metrics.vfov);
|
|
rounded.a = metrics.vfov != 0.0f ? Round100(metrics.hfov / metrics.vfov) : 0;
|
|
rounded.n = Round100(metrics.znear);
|
|
rounded.f = Round100(metrics.zfar);
|
|
|
|
// Some current OpenXR projection paths expose the same Metroid HUD projections at roughly
|
|
// 1/1000th of Hydra's original values. Normalize that compact form so the profile classifier
|
|
// still catches the HUD/visor family while preserving exact Hydra-scale inputs.
|
|
if (std::abs(rounded.h) <= 100 && std::abs(rounded.v) <= 100 &&
|
|
std::abs(rounded.f) <= 1000)
|
|
{
|
|
rounded.h *= 1000;
|
|
rounded.v *= 1000;
|
|
rounded.f *= 1000;
|
|
rounded.n *= 1000;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rounded.l = Round100(metrics.left);
|
|
rounded.r = Round100(metrics.right);
|
|
rounded.t = Round100(metrics.top);
|
|
rounded.b = Round100(metrics.bottom);
|
|
rounded.n = Round100(metrics.znear);
|
|
rounded.f = Round100(metrics.zfar);
|
|
}
|
|
|
|
return rounded;
|
|
}
|
|
|
|
size_t StateIndex(MetroidElementProfile profile)
|
|
{
|
|
switch (profile)
|
|
{
|
|
case MetroidElementProfile::Prime1GC:
|
|
return 0;
|
|
case MetroidElementProfile::Prime1Wii:
|
|
return 1;
|
|
case MetroidElementProfile::Prime2GC:
|
|
return 2;
|
|
case MetroidElementProfile::Prime2Wii:
|
|
return 3;
|
|
case MetroidElementProfile::Prime3:
|
|
return 4;
|
|
case MetroidElementProfile::TrilogyAuto:
|
|
return 5;
|
|
case MetroidElementProfile::None:
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
void MetroidElementClassifier::State::ResetFrame()
|
|
{
|
|
wide_count = 0;
|
|
normal_count = 0;
|
|
after_cursor = false;
|
|
has_thermal_effect = false;
|
|
menu = false;
|
|
}
|
|
|
|
void MetroidElementClassifier::ResetFrame()
|
|
{
|
|
for (State& state : m_states)
|
|
state.ResetFrame();
|
|
}
|
|
|
|
MetroidElementLayer MetroidElementClassifier::Classify(MetroidElementProfile profile,
|
|
const MetroidProjectionMetrics& metrics)
|
|
{
|
|
if (profile != MetroidElementProfile::TrilogyAuto)
|
|
return ClassifyOne(profile, metrics);
|
|
|
|
// Trilogy can route MP1, MP2, or MP3 content through the same title ID. Keep independent
|
|
// subprofile state and prefer layers that an override is likely to act on.
|
|
const MetroidElementLayer prime3 = ClassifyOne(MetroidElementProfile::Prime3, metrics);
|
|
if (!IsWorldLayer(prime3))
|
|
return prime3;
|
|
const MetroidElementLayer prime1 = ClassifyOne(MetroidElementProfile::Prime1Wii, metrics);
|
|
if (!IsWorldLayer(prime1))
|
|
return prime1;
|
|
const MetroidElementLayer prime2 = ClassifyOne(MetroidElementProfile::Prime2Wii, metrics);
|
|
if (!IsWorldLayer(prime2))
|
|
return prime2;
|
|
if (prime3 != MetroidElementLayer::Unknown && prime3 != MetroidElementLayer::Unknown2D)
|
|
return prime3;
|
|
if (prime1 != MetroidElementLayer::Unknown && prime1 != MetroidElementLayer::Unknown2D)
|
|
return prime1;
|
|
return prime2;
|
|
}
|
|
|
|
MetroidElementLayer
|
|
MetroidElementClassifier::ClassifyOne(MetroidElementProfile profile,
|
|
const MetroidProjectionMetrics& metrics)
|
|
{
|
|
const RoundedMetrics rounded = RoundMetrics(metrics);
|
|
switch (profile)
|
|
{
|
|
case MetroidElementProfile::Prime1GC:
|
|
return ClassifyPrime1(false, m_states[StateIndex(profile)], rounded);
|
|
case MetroidElementProfile::Prime1Wii:
|
|
return ClassifyPrime1(true, m_states[StateIndex(profile)], rounded);
|
|
case MetroidElementProfile::Prime2GC:
|
|
case MetroidElementProfile::Prime2Wii:
|
|
return ClassifyPrime2(m_states[StateIndex(profile)], rounded);
|
|
case MetroidElementProfile::Prime3:
|
|
return ClassifyPrime3(m_states[StateIndex(profile)], rounded);
|
|
case MetroidElementProfile::None:
|
|
case MetroidElementProfile::TrilogyAuto:
|
|
default:
|
|
return MetroidElementLayer::Unknown;
|
|
}
|
|
}
|
|
|
|
MetroidElementLayer MetroidElementClassifier::ClassifyPrime1(bool wii, State& state,
|
|
const RoundedMetrics& metrics)
|
|
{
|
|
return metrics.perspective ? ClassifyPrime1Perspective(wii, state, metrics) :
|
|
ClassifyPrime1Ortho(wii, state, metrics);
|
|
}
|
|
|
|
MetroidElementLayer
|
|
MetroidElementClassifier::ClassifyPrime1Perspective(bool wii, State& state,
|
|
const RoundedMetrics& m)
|
|
{
|
|
if (m.layer == 0)
|
|
{
|
|
state.xray_visor = false;
|
|
if (wii)
|
|
state.thermal_visor = false;
|
|
}
|
|
|
|
if (m.layer == 2)
|
|
state.map_screen = false;
|
|
|
|
if ((Near(m.v, m.h, 0) || (wii && Near(m.a, 125, 2))) && m.v < 100 && m.h < 100 &&
|
|
m.layer == 0)
|
|
{
|
|
return MetroidElementLayer::Shadows;
|
|
}
|
|
if (Near(m.v, m.h, 0) && Near(m.v, 5500, 80) && (m.layer == 1 || (!wii && m.layer == 2)))
|
|
return MetroidElementLayer::BodyShadows;
|
|
if (Near(m.f, 300, 20) && Near(m.v, 5500, 80))
|
|
{
|
|
state.is_demo1 = true;
|
|
return MetroidElementLayer::Gun;
|
|
}
|
|
if (Far4096(m) && HudV(m))
|
|
{
|
|
state.cinematic = false;
|
|
if (Near(m.h, 8055, 180) || CompactHudProjection(m))
|
|
{
|
|
state.morphball_active = false;
|
|
state.map_screen = false;
|
|
state.inventory = false;
|
|
return state.xray_visor || state.thermal_visor ? MetroidElementLayer::XRayHUD :
|
|
MetroidElementLayer::ScanText;
|
|
}
|
|
|
|
if (!wii && (m.layer < 5 || !state.has_thermal_effect))
|
|
state.thermal_visor = false;
|
|
|
|
++state.wide_count;
|
|
switch (state.wide_count)
|
|
{
|
|
case 1:
|
|
if (state.map_screen || state.inventory)
|
|
{
|
|
if (state.inventory)
|
|
{
|
|
state.map_screen = false;
|
|
return MetroidElementLayer::Helmet;
|
|
}
|
|
return MetroidElementLayer::MapMap;
|
|
}
|
|
if (state.xray_visor || state.thermal_visor)
|
|
return MetroidElementLayer::VisorRadarHint;
|
|
if (!wii && state.is_demo1 && state.morphball_active)
|
|
return MetroidElementLayer::MorphballMapOrHint;
|
|
if (!wii && state.is_demo1)
|
|
return MetroidElementLayer::Helmet;
|
|
return MetroidElementLayer::HUD;
|
|
case 2:
|
|
if (state.morphball_active)
|
|
return MetroidElementLayer::MorphballMapOrHint;
|
|
if (state.map_screen)
|
|
return MetroidElementLayer::Helmet;
|
|
if (state.xray_visor || state.thermal_visor)
|
|
return MetroidElementLayer::RadarDot;
|
|
if (!wii && state.dark_visor)
|
|
return MetroidElementLayer::Helmet;
|
|
if (state.is_demo1)
|
|
{
|
|
state.morphball_active = false;
|
|
return MetroidElementLayer::HUD;
|
|
}
|
|
return MetroidElementLayer::VisorRadarHint;
|
|
case 3:
|
|
if (state.morphball_active)
|
|
{
|
|
state.scan_visor_active = false;
|
|
return MetroidElementLayer::MorphballMap;
|
|
}
|
|
if (state.xray_visor || state.thermal_visor)
|
|
{
|
|
state.scan_visor_active = false;
|
|
return MetroidElementLayer::Map;
|
|
}
|
|
if (!wii && state.dark_visor)
|
|
{
|
|
state.scan_visor_active = false;
|
|
return MetroidElementLayer::DarkVisorHUD;
|
|
}
|
|
if (state.is_demo1)
|
|
return MetroidElementLayer::VisorRadarHint;
|
|
if (Near(m.h, 8243, 160))
|
|
{
|
|
if (wii && state.scan_visor)
|
|
return MetroidElementLayer::Visor;
|
|
state.scan_visor_active = false;
|
|
return MetroidElementLayer::RadarDot;
|
|
}
|
|
state.scan_visor_active = false;
|
|
return MetroidElementLayer::UnknownVisor;
|
|
case 4:
|
|
if (state.morphball_active)
|
|
return MetroidElementLayer::MorphballMap;
|
|
if (state.scan_visor_active && !Near(m.h, 8243, 160))
|
|
return MetroidElementLayer::ScanHologram;
|
|
if (state.xray_visor || state.thermal_visor)
|
|
return MetroidElementLayer::Helmet;
|
|
if (!wii && (state.dark_visor || state.is_demo1))
|
|
return MetroidElementLayer::RadarDot;
|
|
if (state.scan_visor_active)
|
|
return MetroidElementLayer::UnknownHUD;
|
|
if (Near(m.h, 8243, 160))
|
|
return MetroidElementLayer::Map;
|
|
return MetroidElementLayer::UnknownHUD;
|
|
case 5:
|
|
if (state.dark_visor)
|
|
return MetroidElementLayer::MapOrHint;
|
|
if (state.is_demo1)
|
|
return MetroidElementLayer::Map;
|
|
if (Near(m.h, 8243, 160) && !state.scan_visor_active)
|
|
return MetroidElementLayer::Helmet;
|
|
return MetroidElementLayer::UnknownHUD;
|
|
case 6:
|
|
return state.dark_visor ? MetroidElementLayer::Map : MetroidElementLayer::UnknownHUD;
|
|
default:
|
|
return MetroidElementLayer::UnknownHUD;
|
|
}
|
|
}
|
|
if (Far4096(m) && Near(m.v, 5500, 280))
|
|
{
|
|
if ((state.map_screen || state.inventory) && (Near(m.h, 7327, 180) || Near(m.h, 8564, 220)))
|
|
{
|
|
state.map_screen = false;
|
|
state.inventory = true;
|
|
return MetroidElementLayer::InventorySamus;
|
|
}
|
|
state.scan_visor_active = true;
|
|
return MetroidElementLayer::ScanHologram;
|
|
}
|
|
if (Far4096(m) && Near(m.v, 5443, 280))
|
|
{
|
|
state.map_screen = true;
|
|
return wii || !state.menu ? MetroidElementLayer::MapLegend : MetroidElementLayer::Dialog;
|
|
}
|
|
if (Far750(m) && (Near(m.v, 4958, 180) || Near(m.v, 4522, 180)))
|
|
{
|
|
state.morphball_active = true;
|
|
state.map_screen = false;
|
|
state.scan_visor = false;
|
|
state.inventory = false;
|
|
state.cinematic = false;
|
|
state.thermal_visor = false;
|
|
return MetroidElementLayer::MorphballWorld;
|
|
}
|
|
if (Far750(m) && Near(m.v, 5021, 180))
|
|
{
|
|
state.xray_visor = true;
|
|
state.morphball_active = false;
|
|
state.map_screen = false;
|
|
state.scan_visor = false;
|
|
state.thermal_visor = false;
|
|
state.inventory = false;
|
|
state.cinematic = false;
|
|
return MetroidElementLayer::XRayWorld;
|
|
}
|
|
if (Far750(m) && (Near(m.h, 7327, 180) || Near(m.h, 8564, 220)))
|
|
{
|
|
state.cinematic = false;
|
|
++state.normal_count;
|
|
state.map_screen = false;
|
|
state.inventory = false;
|
|
switch (state.normal_count)
|
|
{
|
|
case 1:
|
|
if (state.xray_visor)
|
|
{
|
|
state.morphball_active = false;
|
|
if (m.layer > 1)
|
|
state.scan_visor = false;
|
|
return wii && Near(m.h, 8564, 220) ? MetroidElementLayer::WiiReticle :
|
|
MetroidElementLayer::Reticle;
|
|
}
|
|
state.morphball_active = false;
|
|
if (m.layer > 1)
|
|
state.scan_visor = false;
|
|
return MetroidElementLayer::World;
|
|
case 2:
|
|
if (state.thermal_visor)
|
|
return MetroidElementLayer::ThermalGunAndDoor;
|
|
if (state.scan_visor)
|
|
return MetroidElementLayer::ScanIcons;
|
|
return wii && Near(m.h, 8564, 220) ? MetroidElementLayer::WiiReticle :
|
|
MetroidElementLayer::Reticle;
|
|
case 3:
|
|
if (state.thermal_visor)
|
|
return wii && Near(m.h, 8564, 220) ? MetroidElementLayer::WiiReticle :
|
|
MetroidElementLayer::Reticle;
|
|
if (state.xray_visor)
|
|
return MetroidElementLayer::UnknownHUD;
|
|
state.scan_visor = true;
|
|
return MetroidElementLayer::ScanCross;
|
|
default:
|
|
return MetroidElementLayer::UnknownWorld;
|
|
}
|
|
}
|
|
if (Far750(m) && Near(m.h, 6594, 140))
|
|
return MetroidElementLayer::WiiWorld;
|
|
if (state.morphball_active && m.v >= 4958 && m.v < 5500)
|
|
{
|
|
state.map_screen = false;
|
|
state.scan_visor = false;
|
|
state.inventory = false;
|
|
state.cinematic = false;
|
|
state.thermal_visor = false;
|
|
return MetroidElementLayer::MorphballWorld;
|
|
}
|
|
|
|
state.cinematic = true;
|
|
state.thermal_visor = false;
|
|
return MetroidElementLayer::CinematicWorld;
|
|
}
|
|
|
|
MetroidElementLayer MetroidElementClassifier::ClassifyPrime1Ortho(bool wii, State& state,
|
|
const RoundedMetrics& m)
|
|
{
|
|
if (state.xray_visor && Prime1XRayEffectProjection(m))
|
|
{
|
|
state.dark_visor = false;
|
|
state.scan_visor = false;
|
|
state.scan_visor_active = false;
|
|
state.morphball_active = false;
|
|
state.thermal_visor = false;
|
|
state.has_thermal_effect = false;
|
|
return MetroidElementLayer::XRayEffect;
|
|
}
|
|
|
|
if (m.layer == 0)
|
|
{
|
|
state.xray_visor = false;
|
|
if (wii)
|
|
state.thermal_visor = false;
|
|
}
|
|
if (std::abs(m.t) == 44800 || std::abs(m.b) == 44800)
|
|
state.vres = 448;
|
|
else if (std::abs(m.t) == 52800 || std::abs(m.b) == 52800)
|
|
state.vres = 528;
|
|
|
|
if (m.layer == 0 && state.map_screen && m.l == 0 && Near(m.n, -100, 20))
|
|
return MetroidElementLayer::Map0;
|
|
if ((m.l == -88 || m.l == -87 || (wii && m.l == -88)) && m.n == 0)
|
|
{
|
|
state.morphball_active = true;
|
|
state.thermal_visor = false;
|
|
return MetroidElementLayer::Shadow2D;
|
|
}
|
|
if (m.l == -32000 && Near(m.n, -409600, 1400) && state.xray_visor)
|
|
{
|
|
state.map_screen = false;
|
|
state.thermal_visor = false;
|
|
return MetroidElementLayer::VisorDirt;
|
|
}
|
|
if (m.l == -32000 && Near(m.n, -409600, 1400) && state.map_screen)
|
|
{
|
|
state.xray_visor = false;
|
|
state.thermal_visor = false;
|
|
return m.layer <= 1 ? MetroidElementLayer::Map1 :
|
|
(m.layer == 2 ? MetroidElementLayer::Map2 : MetroidElementLayer::MapNorth);
|
|
}
|
|
if (m.l == -32000 && (m.t == 22400 || m.t == 26400) && Near(m.n, -409600, 1400) &&
|
|
Near(m.f, 409600, 1400) && (state.cinematic || wii))
|
|
{
|
|
state.thermal_visor = false;
|
|
return wii ? MetroidElementLayer::EFBCopy : MetroidElementLayer::BlackBars;
|
|
}
|
|
if (m.l == -32000 && Near(m.n, -409600, 1400) && Near(m.f, 409600, 1400))
|
|
{
|
|
state.thermal_visor = false;
|
|
if (!wii && m.layer == 4 && !state.scan_visor)
|
|
return MetroidElementLayer::BlackBars;
|
|
if (wii && (m.layer == 3 || m.layer == 4) && !state.scan_visor)
|
|
return MetroidElementLayer::EFBCopy;
|
|
return wii ? MetroidElementLayer::ScanCircle : MetroidElementLayer::ScanDarken;
|
|
}
|
|
if (m.l == -32000 && Near(m.n, -100, 20) && Near(m.f, 100, 20))
|
|
{
|
|
state.thermal_visor = false;
|
|
return MetroidElementLayer::ScanBox;
|
|
}
|
|
if (m.l == -320)
|
|
{
|
|
state.morphball_active = true;
|
|
state.thermal_visor = false;
|
|
return MetroidElementLayer::MorphballHUD;
|
|
}
|
|
if (!wii && m.layer > 2 && m.l == 0 && m.t == 0 && Near(m.n, -409600, 1400) &&
|
|
state.has_thermal_effect)
|
|
{
|
|
state.has_thermal_effect = true;
|
|
state.thermal_visor = true;
|
|
state.dark_visor = false;
|
|
state.scan_visor = false;
|
|
state.scan_visor_active = false;
|
|
state.morphball_active = false;
|
|
state.xray_visor = false;
|
|
return MetroidElementLayer::ThermalEffectGun;
|
|
}
|
|
if (m.l == 0 && m.t == 0 && Near(m.n, -409600, 1400))
|
|
{
|
|
state.has_thermal_effect = true;
|
|
if (state.thermal_visor || wii)
|
|
{
|
|
state.thermal_visor = true;
|
|
state.dark_visor = false;
|
|
state.scan_visor = false;
|
|
state.scan_visor_active = false;
|
|
state.morphball_active = false;
|
|
state.xray_visor = false;
|
|
return m.layer > 2 ? MetroidElementLayer::ThermalEffectGun :
|
|
MetroidElementLayer::ThermalEffect;
|
|
}
|
|
return MetroidElementLayer::ChargeBeamEffect;
|
|
}
|
|
if (state.inventory && m.l == 0 && m.r == 100 && m.t == 100 && Near(m.n, -100, 20) &&
|
|
Near(m.f, 100, 20))
|
|
{
|
|
return MetroidElementLayer::InventorySamusOutline;
|
|
}
|
|
if (m.l == -1570)
|
|
{
|
|
state.menu = true;
|
|
state.scan_visor = false;
|
|
state.thermal_visor = false;
|
|
state.xray_visor = false;
|
|
state.morphball_active = false;
|
|
state.inventory = false;
|
|
state.map_screen = false;
|
|
state.cinematic = false;
|
|
return MetroidElementLayer::Unknown2D;
|
|
}
|
|
if (m.l == -105)
|
|
return MetroidElementLayer::Shadow2D;
|
|
state.scan_visor = false;
|
|
state.echo_visor = false;
|
|
return MetroidElementLayer::Unknown2D;
|
|
}
|
|
|
|
MetroidElementLayer MetroidElementClassifier::ClassifyPrime2(State& state,
|
|
const RoundedMetrics& metrics)
|
|
{
|
|
return metrics.perspective ? ClassifyPrime2Perspective(state, metrics) :
|
|
ClassifyPrime2Ortho(state, metrics);
|
|
}
|
|
|
|
MetroidElementLayer
|
|
MetroidElementClassifier::ClassifyPrime2Perspective(State& state, const RoundedMetrics& m)
|
|
{
|
|
if (m.layer == 1)
|
|
state.scan_visor = false;
|
|
|
|
if (Near(m.v, m.h, 0) && m.layer <= 1)
|
|
return MetroidElementLayer::Shadows;
|
|
if (Far4096(m) && HudV(m))
|
|
{
|
|
++state.wide_count;
|
|
switch (state.wide_count)
|
|
{
|
|
case 1:
|
|
if (state.morphball_active)
|
|
return MetroidElementLayer::MorphballHUD;
|
|
if (state.map_screen)
|
|
return MetroidElementLayer::MapMap;
|
|
if (state.dark_visor && (m.layer == 1 || m.layer == 2))
|
|
return MetroidElementLayer::DarkCentral;
|
|
return MetroidElementLayer::Helmet;
|
|
case 2:
|
|
state.inventory = false;
|
|
if (state.morphball_active)
|
|
return MetroidElementLayer::MorphballMapOrHint;
|
|
if (state.map_screen)
|
|
return MetroidElementLayer::MapLegend;
|
|
if (state.dark_visor)
|
|
return MetroidElementLayer::Helmet;
|
|
return MetroidElementLayer::HUD;
|
|
case 3:
|
|
state.map_screen = false;
|
|
if (state.morphball_active)
|
|
{
|
|
state.scan_visor_active = false;
|
|
return MetroidElementLayer::MorphballMap;
|
|
}
|
|
if (state.dark_visor)
|
|
{
|
|
state.scan_visor_active = false;
|
|
return MetroidElementLayer::DarkVisorHUD;
|
|
}
|
|
if (Near(m.h, 8243, 160))
|
|
{
|
|
state.scan_visor_active = false;
|
|
state.scan_visor = false;
|
|
return MetroidElementLayer::RadarDot;
|
|
}
|
|
if (Near(m.h, 8055, 180) || CompactHudProjection(m))
|
|
{
|
|
state.scan_visor_active = true;
|
|
state.morphball_active = false;
|
|
return MetroidElementLayer::ScanText;
|
|
}
|
|
state.scan_visor_active = false;
|
|
return MetroidElementLayer::UnknownVisor;
|
|
case 4:
|
|
if (state.morphball_active)
|
|
return MetroidElementLayer::MorphballMap;
|
|
if (state.scan_visor_active && !Near(m.h, 8243, 160))
|
|
return MetroidElementLayer::ScanHologram;
|
|
if (state.dark_visor)
|
|
return MetroidElementLayer::RadarDot;
|
|
if (state.scan_visor_active)
|
|
return MetroidElementLayer::UnknownHUD;
|
|
if (Near(m.h, 8243, 160))
|
|
return MetroidElementLayer::MapOrHint;
|
|
return MetroidElementLayer::UnknownHUD;
|
|
case 5:
|
|
if (state.dark_visor)
|
|
return MetroidElementLayer::MapOrHint;
|
|
if (Near(m.h, 8243, 160) && !state.scan_visor_active)
|
|
return MetroidElementLayer::Map;
|
|
return MetroidElementLayer::UnknownHUD;
|
|
case 6:
|
|
return state.dark_visor ? MetroidElementLayer::Map : MetroidElementLayer::UnknownHUD;
|
|
default:
|
|
return MetroidElementLayer::UnknownHUD;
|
|
}
|
|
}
|
|
if (Far4096(m) && Near(m.v, 5500, 280))
|
|
{
|
|
state.scan_visor_active = true;
|
|
return MetroidElementLayer::ScanHologram;
|
|
}
|
|
if (Far4096(m) && Near(m.v, 5443, 280))
|
|
{
|
|
state.map_screen = true;
|
|
return MetroidElementLayer::MapLegend;
|
|
}
|
|
if (Far750(m) && (Near(m.v, 4958, 180) || Near(m.v, 4522, 180)))
|
|
{
|
|
state.morphball_active = true;
|
|
state.map_screen = false;
|
|
state.scan_visor = false;
|
|
state.inventory = false;
|
|
state.cinematic = false;
|
|
return MetroidElementLayer::MorphballWorld;
|
|
}
|
|
if (Far750(m) && (Near(m.h, 7327, 180) || Near(m.h, 8564, 220)))
|
|
{
|
|
state.cinematic = false;
|
|
++state.normal_count;
|
|
state.map_screen = false;
|
|
switch (state.normal_count)
|
|
{
|
|
case 1:
|
|
state.morphball_active = false;
|
|
return MetroidElementLayer::World;
|
|
case 2:
|
|
if (state.scan_visor)
|
|
return MetroidElementLayer::ScanReticle;
|
|
if (state.dark_visor)
|
|
return MetroidElementLayer::UnknownWorld;
|
|
return MetroidElementLayer::Reticle;
|
|
case 3:
|
|
return state.dark_visor ? MetroidElementLayer::Reticle : MetroidElementLayer::UnknownWorld;
|
|
default:
|
|
return MetroidElementLayer::UnknownWorld;
|
|
}
|
|
}
|
|
if (Far4096(m) && m.layer == 4 && state.map_screen)
|
|
{
|
|
state.inventory = true;
|
|
state.cinematic = false;
|
|
return MetroidElementLayer::InventorySamus;
|
|
}
|
|
if (state.morphball_active && m.v >= 4958 && m.v < 5500)
|
|
{
|
|
state.map_screen = false;
|
|
state.scan_visor = false;
|
|
state.inventory = false;
|
|
state.cinematic = false;
|
|
return MetroidElementLayer::MorphballWorld;
|
|
}
|
|
|
|
state.cinematic = true;
|
|
state.scan_visor = false;
|
|
state.map_screen = false;
|
|
return MetroidElementLayer::CinematicWorld;
|
|
}
|
|
|
|
MetroidElementLayer MetroidElementClassifier::ClassifyPrime2Ortho(State& state,
|
|
const RoundedMetrics& m)
|
|
{
|
|
if (std::abs(m.t) == 44800 || std::abs(m.b) == 44800)
|
|
state.vres = 448;
|
|
else if (std::abs(m.t) == 52800 || std::abs(m.b) == 52800)
|
|
state.vres = 528;
|
|
|
|
if (m.layer == 0)
|
|
{
|
|
state.dark_visor = false;
|
|
if (m.l == 0 && m.r == 32000 && (m.t == 22400 || m.t == 26400) &&
|
|
Near(m.n, -100, 20) && Near(m.f, 100, 20))
|
|
{
|
|
state.map_screen = true;
|
|
state.morphball_active = false;
|
|
state.scan_visor = false;
|
|
state.echo_visor = false;
|
|
return MetroidElementLayer::Map0;
|
|
}
|
|
if ((m.l == -75 || m.l == -76) && m.n == 0)
|
|
return MetroidElementLayer::Shadow2D;
|
|
return MetroidElementLayer::Unknown2D;
|
|
}
|
|
if (m.layer == 1)
|
|
{
|
|
state.dark_visor = false;
|
|
if (m.l == -32000 && Near(m.n, -409600, 1400))
|
|
{
|
|
if (state.map_screen)
|
|
{
|
|
state.scan_visor = false;
|
|
state.echo_visor = false;
|
|
state.morphball_active = false;
|
|
return MetroidElementLayer::Map1;
|
|
}
|
|
if (state.cinematic)
|
|
{
|
|
state.scan_visor = false;
|
|
state.scan_visor_active = false;
|
|
return MetroidElementLayer::BlackBars;
|
|
}
|
|
state.scan_visor = true;
|
|
state.echo_visor = false;
|
|
return MetroidElementLayer::ScanHighlighter;
|
|
}
|
|
if (m.l == 0 && Near(m.n, -409600, 1400))
|
|
{
|
|
state.echo_visor = true;
|
|
state.scan_visor = false;
|
|
return MetroidElementLayer::EchoEffect;
|
|
}
|
|
if ((m.l == -75 || m.l == -76) && m.n == 0)
|
|
{
|
|
state.scan_visor = false;
|
|
return MetroidElementLayer::Shadow2D;
|
|
}
|
|
state.scan_visor = false;
|
|
state.echo_visor = false;
|
|
return m.l == -105 ? MetroidElementLayer::Shadow2D : MetroidElementLayer::Unknown2D;
|
|
}
|
|
if ((m.layer == 2 || m.layer == 3) && m.l == 0 && Near(m.n, -409600, 1400))
|
|
{
|
|
if (state.cinematic)
|
|
{
|
|
state.dark_visor = false;
|
|
state.scan_visor = false;
|
|
state.scan_visor_active = false;
|
|
state.morphball_active = false;
|
|
return MetroidElementLayer::Unknown2D;
|
|
}
|
|
state.dark_visor = true;
|
|
state.scan_visor = false;
|
|
state.scan_visor_active = false;
|
|
state.morphball_active = false;
|
|
state.cinematic = false;
|
|
return MetroidElementLayer::DarkEffect;
|
|
}
|
|
if ((m.layer == 2 || m.layer == 3 || m.layer == 4 || m.layer == 5 || m.layer == 6 ||
|
|
m.layer == 7) &&
|
|
m.l == -32000 && (m.t == 22400 || m.t == 26400) && Near(m.n, -409600, 1400) &&
|
|
Near(m.f, 409600, 1400))
|
|
{
|
|
if (state.cinematic)
|
|
return MetroidElementLayer::BlackBars;
|
|
if (state.map_screen && m.layer == 5)
|
|
return MetroidElementLayer::MapNorth;
|
|
if (state.scan_visor && m.layer == 2)
|
|
return MetroidElementLayer::ScreenOverlay;
|
|
if (m.layer == 4 || (m.layer == 5 && state.normal_count == 2))
|
|
return MetroidElementLayer::ScanDarken;
|
|
return MetroidElementLayer::ScreenOverlay;
|
|
}
|
|
if ((m.layer == 4 || m.layer == 5 || m.layer == 6) && m.l == -32000 &&
|
|
Near(m.n, -100, 20) && Near(m.f, 100, 20))
|
|
{
|
|
return MetroidElementLayer::ScanBox;
|
|
}
|
|
if (m.layer == 3 && m.l == 0 && (m.t == 22400 || m.t == 26400) && m.r == 32000 &&
|
|
m.b == 0 && Near(m.n, -100, 20) && Near(m.f, 100, 20))
|
|
{
|
|
return MetroidElementLayer::VisorBootup;
|
|
}
|
|
return MetroidElementLayer::Unknown2D;
|
|
}
|
|
|
|
MetroidElementLayer MetroidElementClassifier::ClassifyPrime3(State& state,
|
|
const RoundedMetrics& metrics)
|
|
{
|
|
return metrics.perspective ? ClassifyPrime3Perspective(state, metrics) :
|
|
ClassifyPrime3Ortho(state, metrics);
|
|
}
|
|
|
|
MetroidElementLayer
|
|
MetroidElementClassifier::ClassifyPrime3Perspective(State& state, const RoundedMetrics& m)
|
|
{
|
|
if (m.layer < 2)
|
|
state.map_screen = false;
|
|
|
|
if ((Near(m.v, m.h, 0) || Near(m.a, 125, 2)) && m.v < 300 && m.h < 300 && m.layer == 0)
|
|
return MetroidElementLayer::Shadows;
|
|
if (Far4096(m) && HudV(m))
|
|
{
|
|
state.cinematic = false;
|
|
if (Near(m.h, 8055, 180) || CompactHudProjection(m))
|
|
{
|
|
state.inventory = false;
|
|
++state.wide_count;
|
|
if (state.morphball_active)
|
|
return MetroidElementLayer::MorphballHUD;
|
|
if (state.after_cursor && !state.map_screen)
|
|
return MetroidElementLayer::Map;
|
|
if (state.wide_count == state.helmet_index && !state.map_screen)
|
|
return MetroidElementLayer::Helmet;
|
|
switch (state.wide_count)
|
|
{
|
|
case 1:
|
|
return m.layer == 2 && state.map_screen ? MetroidElementLayer::Map2 :
|
|
MetroidElementLayer::HUD;
|
|
case 2:
|
|
return state.map_screen ? MetroidElementLayer::Helmet : MetroidElementLayer::RadarDot;
|
|
case 3:
|
|
return state.map_screen ? MetroidElementLayer::MapMap :
|
|
MetroidElementLayer::VisorRadarHint;
|
|
case 4:
|
|
if (state.map_screen)
|
|
return MetroidElementLayer::MapMap;
|
|
state.helmet_index = 4;
|
|
return MetroidElementLayer::Helmet;
|
|
case 5:
|
|
return state.map_screen ? MetroidElementLayer::MapNorth : MetroidElementLayer::Map;
|
|
case 6:
|
|
return state.map_screen ? MetroidElementLayer::MapMap : MetroidElementLayer::UnknownHUD;
|
|
default:
|
|
return MetroidElementLayer::UnknownHUD;
|
|
}
|
|
}
|
|
return MetroidElementLayer::UnknownHUD;
|
|
}
|
|
if (Far750(m) && Near(m.v, 6000, 220))
|
|
{
|
|
state.morphball_active = true;
|
|
state.map_screen = false;
|
|
state.scan_visor = false;
|
|
state.inventory = false;
|
|
state.cinematic = false;
|
|
return MetroidElementLayer::MorphballWorld;
|
|
}
|
|
if (Far750(m) && Near(m.v, 5021, 180))
|
|
{
|
|
state.xray_visor = true;
|
|
state.morphball_active = false;
|
|
state.map_screen = false;
|
|
state.scan_visor = false;
|
|
state.inventory = false;
|
|
state.cinematic = false;
|
|
return MetroidElementLayer::XRayWorld;
|
|
}
|
|
if (Far750(m) && Near(m.h, 8951, 220))
|
|
{
|
|
++state.normal_count;
|
|
state.cinematic = false;
|
|
state.morphball_active = false;
|
|
state.map_screen = false;
|
|
state.inventory = false;
|
|
if (state.wide_count > 0)
|
|
{
|
|
state.after_cursor = true;
|
|
state.helmet_index = state.wide_count;
|
|
return MetroidElementLayer::WiiReticle;
|
|
}
|
|
switch (state.normal_count)
|
|
{
|
|
case 1:
|
|
return MetroidElementLayer::Gun;
|
|
case 2:
|
|
return MetroidElementLayer::World;
|
|
case 3:
|
|
case 4:
|
|
return MetroidElementLayer::Reticle;
|
|
default:
|
|
return MetroidElementLayer::UnknownWorld;
|
|
}
|
|
}
|
|
if (Far750(m) && Near(m.h, 6594, 140))
|
|
return MetroidElementLayer::WiiWorld;
|
|
if (Near(m.h, 5830, 140) && Near(m.v, 3264, 140) && Near(m.f, 7500, 100))
|
|
return MetroidElementLayer::UnknownWorld;
|
|
if (Near(m.h, 8213, 180) && Near(m.v, 5400, 180) && Near(m.n, 100, 20) &&
|
|
Far4096(m))
|
|
return MetroidElementLayer::UnknownWorld;
|
|
|
|
state.cinematic = true;
|
|
state.morphball_active = false;
|
|
return MetroidElementLayer::CinematicWorld;
|
|
}
|
|
|
|
MetroidElementLayer MetroidElementClassifier::ClassifyPrime3Ortho(State& state,
|
|
const RoundedMetrics& m)
|
|
{
|
|
if (std::abs(m.t) == 44800 || std::abs(m.b) == 44800)
|
|
state.vres = 448;
|
|
else if (std::abs(m.t) == 52800 || std::abs(m.b) == 52800)
|
|
state.vres = 528;
|
|
|
|
const int half_vres = state.vres * 50;
|
|
const int full_vres = state.vres * 100;
|
|
if (m.l == -32000 && m.t == half_vres && m.r == 32000 && m.b == -half_vres &&
|
|
Near(m.n, -100, 20) && Near(m.f, -1000, 50))
|
|
{
|
|
return MetroidElementLayer::Unknown2D;
|
|
}
|
|
if (m.layer == 0 && m.l == 0 && m.t == half_vres && m.r == 32000 && m.b == 0 &&
|
|
Near(m.n, -100, 20) && Near(m.f, 100, 20))
|
|
{
|
|
state.map_screen = true;
|
|
state.morphball_active = false;
|
|
return MetroidElementLayer::Map0;
|
|
}
|
|
if (m.layer == 1 && m.l == -3200 && m.r == 3200 && m.t == 3200 && m.b == -3200 &&
|
|
Near(m.n, -100, 20) && Near(m.f, -1000, 50))
|
|
{
|
|
return MetroidElementLayer::Shadow2D;
|
|
}
|
|
if (m.layer == 1 && m.l == -32000 && m.t == half_vres && m.r == 32000 &&
|
|
m.b == -half_vres && Near(m.n, -409600, 1400) && Near(m.f, 409600, 1400))
|
|
{
|
|
state.map_screen = true;
|
|
state.morphball_active = false;
|
|
return MetroidElementLayer::Map1;
|
|
}
|
|
if (m.layer > 0 && m.l == 0 && m.t == full_vres && m.r == 64000 && m.b == 0 &&
|
|
Near(m.n, -409600, 1400) && Near(m.f, 409600, 1400))
|
|
{
|
|
return MetroidElementLayer::EFBCopy;
|
|
}
|
|
if (m.layer > 1 && m.l == 0 && m.t == 0 && m.r == 640 && m.b == 528 &&
|
|
Near(m.n, -100, 20) && Near(m.f, 100, 20))
|
|
{
|
|
return MetroidElementLayer::MousePointer;
|
|
}
|
|
if (m.layer > 1 && m.l == -32000 && m.t == half_vres && m.r == 32000 &&
|
|
m.b == -half_vres && Near(m.n, -409600, 1400) && Near(m.f, 409600, 1400))
|
|
{
|
|
return MetroidElementLayer::ScreenFade;
|
|
}
|
|
return MetroidElementLayer::Unknown2D;
|
|
}
|
|
|
|
std::string_view MetroidElementProfileToININame(MetroidElementProfile profile)
|
|
{
|
|
for (const ProfileName& name : PROFILE_NAMES)
|
|
{
|
|
if (name.profile == profile)
|
|
return name.ini_name;
|
|
}
|
|
return "none";
|
|
}
|
|
|
|
std::string_view MetroidElementProfileToDisplayName(MetroidElementProfile profile)
|
|
{
|
|
for (const ProfileName& name : PROFILE_NAMES)
|
|
{
|
|
if (name.profile == profile)
|
|
return name.display_name;
|
|
}
|
|
return "None";
|
|
}
|
|
|
|
std::optional<MetroidElementProfile> MetroidElementProfileFromString(std::string_view value)
|
|
{
|
|
const std::string normalized = NormalizeKey(value);
|
|
for (const ProfileName& name : PROFILE_NAMES)
|
|
{
|
|
if (NormalizeKey(name.ini_name) == normalized || NormalizeKey(name.display_name) == normalized)
|
|
return name.profile;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::vector<MetroidElementProfile> GetMetroidElementProfiles()
|
|
{
|
|
std::vector<MetroidElementProfile> profiles;
|
|
profiles.reserve(PROFILE_NAMES.size());
|
|
for (const ProfileName& name : PROFILE_NAMES)
|
|
profiles.push_back(name.profile);
|
|
return profiles;
|
|
}
|
|
|
|
std::string_view MetroidElementLayerToININame(MetroidElementLayer layer)
|
|
{
|
|
for (const LayerName& name : LAYER_NAMES)
|
|
{
|
|
if (name.layer == layer)
|
|
return name.ini_name;
|
|
}
|
|
return "METROID_UNKNOWN";
|
|
}
|
|
|
|
std::string_view MetroidElementLayerToDisplayName(MetroidElementLayer layer)
|
|
{
|
|
for (const LayerName& name : LAYER_NAMES)
|
|
{
|
|
if (name.layer == layer)
|
|
return name.display_name;
|
|
}
|
|
return "Unknown";
|
|
}
|
|
|
|
std::optional<MetroidElementLayer> MetroidElementLayerFromString(std::string_view value)
|
|
{
|
|
const std::string normalized = NormalizeKey(value);
|
|
for (const LayerName& name : LAYER_NAMES)
|
|
{
|
|
if (NormalizeKey(name.ini_name) == normalized || NormalizeKey(name.display_name) == normalized)
|
|
return name.layer;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::vector<MetroidElementLayer> GetMetroidElementLayers()
|
|
{
|
|
std::vector<MetroidElementLayer> layers;
|
|
layers.reserve(LAYER_NAMES.size());
|
|
for (const LayerName& name : LAYER_NAMES)
|
|
layers.push_back(name.layer);
|
|
return layers;
|
|
}
|