├── .gitignore ├── Config ├── ConfigurationManagerAttributes.cs └── Settings.cs ├── Data └── Defs.cs ├── DynamicMaps.csproj ├── DynamicMaps.sln ├── DynamicMarkers ├── AirdropMarkerProvider.cs ├── BTRMarkerProvider.cs ├── BackpackMarkerProvider.cs ├── CorpseMarkerProvider.cs ├── ExtractMarkerProvider.cs ├── IDynamicMarkerProvider.cs ├── LockedDoorMarkerMutator.cs ├── OtherPlayersMarkerProvider.cs ├── PlayerMarkerProvider.cs └── QuestMarkerProvider.cs ├── FEATURE_WISHLIST.md ├── KNOWN_ISSUES.md ├── LICENSE.txt ├── Patches ├── AirdropBoxPatches.cs ├── BattleUIScreenPatches.cs ├── CommonUIPatches.cs ├── GameWorldPatches.cs ├── MapScreenPatches.cs └── PlayerPatches.cs ├── Plugin.cs ├── README.md ├── Resources ├── Maps │ ├── Customs_TarkovDev │ │ ├── Customs_TarkovDev.jsonc │ │ └── Layers │ │ │ ├── SVG │ │ │ ├── Customs-Ground_Level.svg │ │ │ ├── Customs-Second_Floor.svg │ │ │ ├── Customs-Third_Floor.svg │ │ │ ├── Customs-Underground_Level.svg │ │ │ └── Customs.svg │ │ │ ├── customs_level_-1.png │ │ │ ├── customs_level_0.png │ │ │ ├── customs_level_1.png │ │ │ └── customs_level_2.png │ ├── Factory_TarkovDev │ │ ├── Factory_TarkovDev.jsonc │ │ └── Layers │ │ │ ├── SVG │ │ │ ├── Factory-Basement.svg │ │ │ ├── Factory-Ground_Floor.svg │ │ │ ├── Factory-Second_Floor.svg │ │ │ ├── Factory-Third_Floor.svg │ │ │ └── Factory.svg │ │ │ ├── factory_layer_-1.png │ │ │ ├── factory_layer_0.png │ │ │ ├── factory_layer_1.png │ │ │ └── factory_layer_2.png │ ├── GroundZero_TarkovDev │ │ ├── GroundZero_TarkovDev.jsonc │ │ └── Layers │ │ │ ├── SVG │ │ │ ├── GroundZero-Ground_Level.svg │ │ │ ├── GroundZero-Second_Floor.svg │ │ │ ├── GroundZero-Third_Floor.svg │ │ │ ├── GroundZero-Underground_Level.svg │ │ │ └── GroundZero.svg │ │ │ ├── ground_zero_layer_-1.png │ │ │ ├── ground_zero_layer_0.png │ │ │ ├── ground_zero_layer_1.png │ │ │ └── ground_zero_layer_2.png │ ├── Interchange_TarkovDev │ │ ├── Interchange_TarkovDev.jsonc │ │ └── Layers │ │ │ ├── SVG │ │ │ ├── Interchange-First_Floor.svg │ │ │ ├── Interchange-Ground_Level.svg │ │ │ ├── Interchange-Second_Floor.svg │ │ │ └── Interchange.svg │ │ │ ├── interchange_layer_0.png │ │ │ ├── interchange_layer_1.png │ │ │ └── interchange_layer_2.png │ ├── Labs_TarkovDev │ │ ├── Labs_TarkovDev.jsonc │ │ └── Layers │ │ │ ├── SVG │ │ │ ├── Labs.svg │ │ │ ├── Labs_First_Level.svg │ │ │ ├── Labs_Second_Level.svg │ │ │ └── Labs_Technical_Level.svg │ │ │ ├── labs_layer_-1.png │ │ │ ├── labs_layer_0.png │ │ │ └── labs_layer_1.png │ ├── Lighthouse_TarkovData │ │ ├── Layers │ │ │ ├── SVG │ │ │ │ └── Lighthouse.svg │ │ │ └── lighthouse_layer_0.png │ │ └── Lighthouse_TarkovData.jsonc │ ├── Reserve_TarkovData │ │ ├── Layers │ │ │ ├── SVG │ │ │ │ ├── Reserve-Bunkers.svg │ │ │ │ ├── Reserve-Ground_Level.svg │ │ │ │ └── Reserve.svg │ │ │ ├── reserve_layer_-1.png │ │ │ └── reserve_layer_0.png │ │ └── Reserve_TarkovData.jsonc │ ├── Shoreline_TarkovData │ │ ├── Layers │ │ │ ├── SVG │ │ │ │ ├── Shoreline-Ground_Level.svg │ │ │ │ ├── Shoreline-Second_Floor.svg │ │ │ │ ├── Shoreline-Third_Floor.svg │ │ │ │ ├── Shoreline-Underground_Level.svg │ │ │ │ └── Shoreline.svg │ │ │ ├── shoreline_layer_-1.png │ │ │ ├── shoreline_layer_0.png │ │ │ ├── shoreline_layer_1.png │ │ │ └── shoreline_layer_2.png │ │ └── Shoreline_TarkovData.jsonc │ ├── Streets_TarkovData │ │ ├── Layers │ │ │ ├── SVG │ │ │ │ ├── StreetsOfTarkov-Fifth_Floor.svg │ │ │ │ ├── StreetsOfTarkov-Fourth_Floor.svg │ │ │ │ ├── StreetsOfTarkov-Ground_Level.svg │ │ │ │ ├── StreetsOfTarkov-Second_Floor.svg │ │ │ │ ├── StreetsOfTarkov-Third_Floor.svg │ │ │ │ ├── StreetsOfTarkov-Underground_Level.svg │ │ │ │ └── StreetsOfTarkov.svg │ │ │ ├── streets_layer_-1.png │ │ │ ├── streets_layer_0.png │ │ │ ├── streets_layer_1.png │ │ │ ├── streets_layer_2.png │ │ │ ├── streets_layer_3.png │ │ │ └── streets_layer_4.png │ │ └── Streets_TarkovData.jsonc │ ├── Woods_TarkovData │ │ ├── Layers │ │ │ ├── SVG │ │ │ │ └── Woods.svg │ │ │ └── woods_layer_0.png │ │ └── Woods_TarkovData.jsonc │ └── map_and_data_credits.txt └── Markers │ ├── airdrop.png │ ├── arrow.png │ ├── backpack.png │ ├── btr.png │ ├── car_door.png │ ├── car_key.png │ ├── door_with_key.png │ ├── door_with_lock.png │ ├── dot.png │ ├── exit.png │ ├── lever.png │ ├── marker_credits.txt │ ├── quest.png │ ├── skull.png │ └── star.png ├── Screenshots ├── airdrop_marker.png ├── btr_marker.png ├── corpse_markers.png ├── dynamic_locks.png ├── in_raid_map.png ├── out_of_raid_map.png └── quest_markers.png ├── UI ├── Components │ ├── MapLabel.cs │ ├── MapLayer.cs │ ├── MapMarker.cs │ ├── MapPeekComponent.cs │ ├── MapView.cs │ ├── PlayerMapMarker.cs │ └── TransformMapMarker.cs ├── Controls │ ├── AbstractTextControl.cs │ ├── CursorPositionText.cs │ ├── LevelSelectSlider.cs │ ├── MapSelectDropdown.cs │ └── PlayerPositionText.cs └── ModdedMapScreen.cs ├── Utils ├── DumpUtils.cs ├── GameUtils.cs ├── MathUtils.cs ├── PlayerDotSpawner.cs ├── QuestUtils.cs ├── TextureUtils.cs └── UIUtils.cs ├── VERSION.txt └── VersionChecker.cs /Config/ConfigurationManagerAttributes.cs: -------------------------------------------------------------------------------- 1 | /// 2 | /// Class that specifies how a setting should be displayed inside the ConfigurationManager settings window. 3 | /// 4 | /// Usage: 5 | /// This class template has to be copied inside the plugin's project and referenced by its code directly. 6 | /// make a new instance, assign any fields that you want to override, and pass it as a tag for your setting. 7 | /// 8 | /// If a field is null (default), it will be ignored and won't change how the setting is displayed. 9 | /// If a field is non-null (you assigned a value to it), it will override default behavior. 10 | /// 11 | /// 12 | /// 13 | /// Here's an example of overriding order of settings and marking one of the settings as advanced: 14 | /// 15 | /// // Override IsAdvanced and Order 16 | /// Config.Bind("X", "1", 1, new ConfigDescription("", null, new ConfigurationManagerAttributes { IsAdvanced = true, Order = 3 })); 17 | /// // Override only Order, IsAdvanced stays as the default value assigned by ConfigManager 18 | /// Config.Bind("X", "2", 2, new ConfigDescription("", null, new ConfigurationManagerAttributes { Order = 1 })); 19 | /// Config.Bind("X", "3", 3, new ConfigDescription("", null, new ConfigurationManagerAttributes { Order = 2 })); 20 | /// 21 | /// 22 | /// 23 | /// 24 | /// You can read more and see examples in the readme at https://github.com/BepInEx/BepInEx.ConfigurationManager 25 | /// You can optionally remove fields that you won't use from this class, it's the same as leaving them null. 26 | /// 27 | #pragma warning disable 0169, 0414, 0649 28 | internal sealed class ConfigurationManagerAttributes 29 | { 30 | /// 31 | /// Should the setting be shown as a percentage (only use with value range settings). 32 | /// 33 | public bool? ShowRangeAsPercent; 34 | 35 | /// 36 | /// Custom setting editor (OnGUI code that replaces the default editor provided by ConfigurationManager). 37 | /// See below for a deeper explanation. Using a custom drawer will cause many of the other fields to do nothing. 38 | /// 39 | public System.Action CustomDrawer; 40 | 41 | /// 42 | /// Custom setting editor that allows polling keyboard input with the Input (or UnityInput) class. 43 | /// Use either CustomDrawer or CustomHotkeyDrawer, using both at the same time leads to undefined behaviour. 44 | /// 45 | public CustomHotkeyDrawerFunc CustomHotkeyDrawer; 46 | 47 | /// 48 | /// Custom setting draw action that allows polling keyboard input with the Input class. 49 | /// Note: Make sure to focus on your UI control when you are accepting input so user doesn't type in the search box or in another setting (best to do this on every frame). 50 | /// If you don't draw any selectable UI controls You can use `GUIUtility.keyboardControl = -1;` on every frame to make sure that nothing is selected. 51 | /// 52 | /// 53 | /// CustomHotkeyDrawer = (ConfigEntryBase setting, ref bool isEditing) => 54 | /// { 55 | /// if (isEditing) 56 | /// { 57 | /// // Make sure nothing else is selected since we aren't focusing on a text box with GUI.FocusControl. 58 | /// GUIUtility.keyboardControl = -1; 59 | /// 60 | /// // Use Input.GetKeyDown and others here, remember to set isEditing to false after you're done! 61 | /// // It's best to check Input.anyKeyDown and set isEditing to false immediately if it's true, 62 | /// // so that the input doesn't have a chance to propagate to the game itself. 63 | /// 64 | /// if (GUILayout.Button("Stop")) 65 | /// isEditing = false; 66 | /// } 67 | /// else 68 | /// { 69 | /// if (GUILayout.Button("Start")) 70 | /// isEditing = true; 71 | /// } 72 | /// 73 | /// // This will only be true when isEditing is true and you hold any key 74 | /// GUILayout.Label("Any key pressed: " + Input.anyKey); 75 | /// } 76 | /// 77 | /// 78 | /// Setting currently being set (if available). 79 | /// 80 | /// 81 | /// Set this ref parameter to true when you want the current setting drawer to receive Input events. 82 | /// The value will persist after being set, use it to see if the current instance is being edited. 83 | /// Remember to set it to false after you are done! 84 | /// 85 | public delegate void CustomHotkeyDrawerFunc(BepInEx.Configuration.ConfigEntryBase setting, ref bool isCurrentlyAcceptingInput); 86 | 87 | /// 88 | /// Show this setting in the settings screen at all? If false, don't show. 89 | /// 90 | public bool? Browsable; 91 | 92 | /// 93 | /// Category the setting is under. Null to be directly under the plugin. 94 | /// 95 | public string Category; 96 | 97 | /// 98 | /// If set, a "Default" button will be shown next to the setting to allow resetting to default. 99 | /// 100 | public object DefaultValue; 101 | 102 | /// 103 | /// Force the "Reset" button to not be displayed, even if a valid DefaultValue is available. 104 | /// 105 | public bool? HideDefaultButton; 106 | 107 | /// 108 | /// Force the setting name to not be displayed. Should only be used with a to get more space. 109 | /// Can be used together with to gain even more space. 110 | /// 111 | public bool? HideSettingName; 112 | 113 | /// 114 | /// Optional description shown when hovering over the setting. 115 | /// Not recommended, provide the description when creating the setting instead. 116 | /// 117 | public string Description; 118 | 119 | /// 120 | /// Name of the setting. 121 | /// 122 | public string DispName; 123 | 124 | /// 125 | /// Order of the setting on the settings list relative to other settings in a category. 126 | /// 0 by default, higher number is higher on the list. 127 | /// 128 | public int? Order; 129 | 130 | /// 131 | /// Only show the value, don't allow editing it. 132 | /// 133 | public bool? ReadOnly; 134 | 135 | /// 136 | /// If true, don't show the setting by default. User has to turn on showing advanced settings or search for it. 137 | /// 138 | public bool? IsAdvanced; 139 | 140 | /// 141 | /// Custom converter from setting type to string for the built-in editor textboxes. 142 | /// 143 | public System.Func ObjToStr; 144 | 145 | /// 146 | /// Custom converter from string to setting type for the built-in editor textboxes. 147 | /// 148 | public System.Func StrToObj; 149 | } 150 | -------------------------------------------------------------------------------- /Data/Defs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Newtonsoft.Json; 5 | using UnityEngine; 6 | 7 | namespace DynamicMaps.Data 8 | { 9 | public class BoundingRectangle 10 | { 11 | public Vector2 Min { get; set; } 12 | public Vector2 Max { get; set; } 13 | } 14 | 15 | public class BoundingRectangularSolid 16 | { 17 | public Vector3 Min { get; set; } 18 | public Vector3 Max { get; set; } 19 | } 20 | 21 | public class MapLayerDef 22 | { 23 | [JsonRequired] 24 | public int Level { get; set; } 25 | 26 | [JsonRequired] 27 | public string ImagePath { get; set; } 28 | 29 | [JsonRequired] 30 | public BoundingRectangle ImageBounds { get; set; } 31 | 32 | [JsonRequired] 33 | public List GameBounds { get; set; } 34 | } 35 | 36 | public class MapMarkerDef 37 | { 38 | public string Text { get; set; } = ""; 39 | 40 | [JsonRequired] 41 | public string ImagePath { get; set; } 42 | 43 | [JsonRequired] 44 | public Vector3 Position { get; set; } 45 | 46 | public bool ShowInRaid { get; set; } = true; 47 | public string Category { get; set; } = "None"; 48 | public Color Color { get; set; } = Color.white; 49 | public Vector2 Pivot { get; set; } = new Vector2(0.5f, 0.5f); 50 | public string AssociatedItemId { get; set; } = ""; 51 | } 52 | 53 | public class MapLabelDef 54 | { 55 | [JsonRequired] 56 | public string Text { get; set; } 57 | 58 | [JsonRequired] 59 | public Vector3 Position { get; set; } 60 | 61 | public float FontSize { get; set; } = 14f; 62 | public float DegreesRotation { get; set; } = 0f; 63 | public Color Color { get; set; } = Color.white; 64 | public string Category { get; set; } = "None"; 65 | } 66 | 67 | public class MapDef 68 | { 69 | [JsonRequired] 70 | public string DisplayName { get; set; } 71 | 72 | [JsonRequired] 73 | public BoundingRectangle Bounds { get; set; } 74 | 75 | [JsonRequired] 76 | public Dictionary Layers { get; set; } = new Dictionary(); 77 | 78 | public List MapInternalNames { get; set; } = new List(); 79 | public float CoordinateRotation { get; set; } = 0; 80 | public List Labels { get; set; } = new List(); 81 | public List StaticMarkers { get; set; } = new List(); 82 | public int DefaultLevel { get; set; } = 0; 83 | 84 | public string Author { get; set; } 85 | public string AuthorLink { get; set; } 86 | 87 | public static MapDef LoadFromPath(string absolutePath) 88 | { 89 | try 90 | { 91 | return JsonConvert.DeserializeObject(File.ReadAllText(absolutePath)); 92 | } 93 | catch (Exception e) 94 | { 95 | Plugin.Log.LogError($"Loading MapMappingDef failed from json at path: {absolutePath}"); 96 | Plugin.Log.LogError($"Exception given was: {e.Message}"); 97 | Plugin.Log.LogError($"{e.StackTrace}"); 98 | } 99 | 100 | return null; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /DynamicMaps.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net471 4 | DynamicMaps 5 | Dynamic in-game maps for SPT 6 | 7.3 7 | disable 8 | true 9 | 10 | 11 | 12 | D:\Games\SPTarkov-3.9.0\ 13 | D:\Games\SPTarkov-3.9.0-Debug\ 14 | $(TarkovDir)BepInEx\plugins\spt\ 15 | $(TarkovDir)EscapeFromTarkov_Data\Managed\ 16 | $(TarkovDir)BepInEx\core\ 17 | 18 | 19 | 20 | 21 | $(TarkovManagedDir)Assembly-CSharp.dll 22 | 23 | 24 | $(TarkovManagedDir)Comfort.dll 25 | 26 | 27 | $(TarkovManagedDir)Comfort.Unity.dll 28 | 29 | 30 | $(TarkovManagedDir)DissonanceVoip.dll 31 | 32 | 33 | $(TarkovManagedDir)DOTween.dll 34 | 35 | 36 | $(TarkovManagedDir)DOTween.Modules.dll 37 | 38 | 39 | $(TarkovManagedDir)Newtonsoft.Json.dll 40 | 41 | 42 | $(TarkovManagedDir)Sirenix.Serialization.dll 43 | 44 | 45 | $(TarkovManagedDir)Unity.TextMeshPro.dll 46 | 47 | 48 | $(TarkovManagedDir)UnityEngine.dll 49 | 50 | 51 | $(TarkovManagedDir)UnityEngine.CoreModule.dll 52 | 53 | 54 | $(TarkovManagedDir)UnityEngine.PhysicsModule.dll 55 | 56 | 57 | $(TarkovManagedDir)UnityEngine.ImageConversionModule.dll 58 | 59 | 60 | $(TarkovManagedDir)UnityEngine.InputLegacyModule.dll 61 | 62 | 63 | $(TarkovManagedDir)UnityEngine.IMGUIModule.dll 64 | 65 | 66 | $(TarkovManagedDir)UnityEngine.TextRenderingModule.dll 67 | 68 | 69 | $(TarkovManagedDir)UnityEngine.UI.dll 70 | 71 | 72 | $(TarkovManagedDir)UnityEngine.UIModule.dll 73 | 74 | 75 | 76 | $(TarkovBepInExCoreDir)0Harmony.dll 77 | 78 | 79 | $(TarkovBepInExCoreDir)BepInEx.dll 80 | 81 | 82 | $(TarkovBepInExCoreDir)BepInEx.Preloader.dll 83 | 84 | 85 | 86 | $(TarkovPluginsSPTDir)spt-reflection.dll 87 | 88 | 89 | $(TarkovPluginsSPTDir)spt-custom.dll 90 | 91 | 92 | 93 | 94 | 95 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 134 | 138 | 139 | 140 | 141 | 145 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /DynamicMaps.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.0.31903.59 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicMaps", "DynamicMaps.csproj", "{49EEA63A-07E0-4CA2-8EA8-22A42E269490}" 6 | EndProject 7 | Global 8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(SolutionProperties) = preSolution 12 | HideSolutionNode = FALSE 13 | EndGlobalSection 14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 15 | {49EEA63A-07E0-4CA2-8EA8-22A42E269490}.Release|Any CPU.ActiveCfg = Release|Any CPU 16 | {49EEA63A-07E0-4CA2-8EA8-22A42E269490}.Release|Any CPU.Build.0 = Release|Any CPU 17 | EndGlobalSection 18 | EndGlobal 19 | -------------------------------------------------------------------------------- /DynamicMarkers/AirdropMarkerProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using SPT.Custom.Airdrops; 4 | using DynamicMaps.Data; 5 | using DynamicMaps.Patches; 6 | using DynamicMaps.UI.Components; 7 | using DynamicMaps.Utils; 8 | using UnityEngine; 9 | 10 | namespace DynamicMaps.DynamicMarkers 11 | { 12 | public class AirdropMarkerProvider : IDynamicMarkerProvider 13 | { 14 | private MapView _lastMapView; 15 | private Dictionary _airdropMarkers = new Dictionary(); 16 | 17 | // TODO: move to config 18 | private const string _airdropName = "Airdrop"; 19 | private const string _airdropCategory = "Airdrop"; 20 | private const string _airdropImagePath = "Markers/airdrop.png"; 21 | private static Vector2 _airdropPivot = new Vector2(0.5f, 0.25f); 22 | private static Color _airdropColor = Color.Lerp(Color.red, Color.white, 0.333f); 23 | // 24 | 25 | public void OnShowInRaid(MapView map) 26 | { 27 | _lastMapView = map; 28 | 29 | // add all existing airdrops 30 | foreach (var airdrop in AirdropBoxOnBoxLandPatch.Airdrops) 31 | { 32 | TryAddMarker(airdrop); 33 | } 34 | 35 | // subscribe to new airdrops while map is open 36 | AirdropBoxOnBoxLandPatch.OnAirdropLanded += TryAddMarker; 37 | } 38 | 39 | public void OnHideInRaid(MapView map) 40 | { 41 | // unsubscribe to new airdrops, since we're hiding 42 | AirdropBoxOnBoxLandPatch.OnAirdropLanded -= TryAddMarker; 43 | } 44 | 45 | public void OnRaidEnd(MapView map) 46 | { 47 | TryRemoveMarkers(); 48 | } 49 | 50 | public void OnMapChanged(MapView map, MapDef mapDef) 51 | { 52 | _lastMapView = map; 53 | 54 | // transition markers from last map to this one 55 | foreach (var airdrop in _airdropMarkers.Keys.ToList()) 56 | { 57 | TryRemoveMarker(airdrop); 58 | TryAddMarker(airdrop); 59 | } 60 | } 61 | 62 | public void OnDisable(MapView map) 63 | { 64 | OnRaidEnd(map); 65 | } 66 | 67 | private void TryAddMarker(AirdropBox airdrop) 68 | { 69 | if (_airdropMarkers.ContainsKey(airdrop)) 70 | { 71 | return; 72 | } 73 | 74 | var markerDef = new MapMarkerDef 75 | { 76 | Category = _airdropCategory, 77 | Color = _airdropColor, 78 | ImagePath = _airdropImagePath, 79 | Position = MathUtils.ConvertToMapPosition(airdrop.transform), 80 | Pivot = _airdropPivot, 81 | Text = _airdropName 82 | }; 83 | 84 | var marker = _lastMapView.AddMapMarker(markerDef); 85 | _airdropMarkers[airdrop] = marker; 86 | } 87 | 88 | private void TryRemoveMarkers() 89 | { 90 | foreach (var airdrop in _airdropMarkers.Keys.ToList()) 91 | { 92 | TryRemoveMarker(airdrop); 93 | } 94 | } 95 | 96 | private void TryRemoveMarker(AirdropBox airdrop) 97 | { 98 | if (!_airdropMarkers.ContainsKey(airdrop)) 99 | { 100 | return; 101 | } 102 | 103 | _airdropMarkers[airdrop].ContainingMapView.RemoveMapMarker(_airdropMarkers[airdrop]); 104 | _airdropMarkers.Remove(airdrop); 105 | } 106 | 107 | public void OnShowOutOfRaid(MapView map) 108 | { 109 | // do nothing 110 | } 111 | 112 | public void OnHideOutOfRaid(MapView map) 113 | { 114 | // do nothing 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /DynamicMarkers/BTRMarkerProvider.cs: -------------------------------------------------------------------------------- 1 | using DynamicMaps.Data; 2 | using DynamicMaps.UI.Components; 3 | using DynamicMaps.Utils; 4 | using UnityEngine; 5 | 6 | namespace DynamicMaps.DynamicMarkers 7 | { 8 | public class BTRMarkerProvider : IDynamicMarkerProvider 9 | { 10 | // TODO: move to config 11 | private static string _btrIconPath = "Markers/btr.png"; 12 | private static Color _btrColor = new Color(54f/255f, 100f/255f, 42f/255f); 13 | private static Vector2 _btrSize = new Vector2(45, 45f); 14 | private static string _btrName = "BTR"; 15 | private static string _btrCategory = "BTR"; 16 | // 17 | 18 | private MapMarker _btrMarker; 19 | 20 | public void OnShowInRaid(MapView map) 21 | { 22 | if (_btrMarker != null) 23 | { 24 | return; 25 | } 26 | 27 | TryAddMarker(map); 28 | } 29 | 30 | public void OnMapChanged(MapView map, MapDef mapDef) 31 | { 32 | TryRemoveMarker(); 33 | TryAddMarker(map); 34 | } 35 | 36 | public void OnRaidEnd(MapView map) 37 | { 38 | TryRemoveMarker(); 39 | } 40 | 41 | public void OnDisable(MapView map) 42 | { 43 | TryRemoveMarker(); 44 | } 45 | 46 | private void TryAddMarker(MapView map) 47 | { 48 | if (_btrMarker != null) 49 | { 50 | return; 51 | } 52 | 53 | var btrView = GameUtils.GetBTRView(); 54 | if (btrView == null) 55 | { 56 | return; 57 | } 58 | 59 | // try adding the marker 60 | _btrMarker = map.AddTransformMarker(btrView.transform, _btrName, _btrCategory, _btrColor, 61 | _btrIconPath, _btrSize); 62 | } 63 | 64 | private void TryRemoveMarker() 65 | { 66 | if (_btrMarker == null) 67 | { 68 | return; 69 | } 70 | 71 | _btrMarker.ContainingMapView.RemoveMapMarker(_btrMarker); 72 | _btrMarker = null; 73 | } 74 | 75 | public void OnHideInRaid(MapView map) 76 | { 77 | // do nothing 78 | } 79 | 80 | public void OnShowOutOfRaid(MapView map) 81 | { 82 | // do nothing 83 | } 84 | 85 | public void OnHideOutOfRaid(MapView map) 86 | { 87 | // do nothing 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /DynamicMarkers/BackpackMarkerProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Comfort.Common; 4 | using DynamicMaps.Data; 5 | using DynamicMaps.Patches; 6 | using DynamicMaps.UI.Components; 7 | using DynamicMaps.Utils; 8 | using EFT; 9 | using EFT.Interactive; 10 | using EFT.UI.DragAndDrop; 11 | using UnityEngine; 12 | 13 | namespace DynamicMaps.DynamicMarkers 14 | { 15 | public class BackpackMarkerProvider : IDynamicMarkerProvider 16 | { 17 | // TODO: move to config 18 | private const string _backpackCategory = "Backpack"; 19 | private const string _backpackImagePath = "Markers/backpack.png"; 20 | private static Color _backpackColor = Color.green; 21 | private static Vector2 _backpackSize = new Vector2(30f, 30f); 22 | // 23 | 24 | private MapView _lastMapView; 25 | private Dictionary _backpackMarkers = new Dictionary(); 26 | 27 | public void OnShowInRaid(MapView map) 28 | { 29 | _lastMapView = map; 30 | 31 | RemoveStaleMarkers(); 32 | AddThrownBackpacks(); 33 | 34 | // register for if an item is registered 35 | GameWorldRegisterLootItemPatch.OnRegisterLoot += OnRegisterLoot; 36 | 37 | // register for if item is destroyed (probably picked up) 38 | GameWorldDestroyLootPatch.OnDestroyLoot += OnDestroyLoot; 39 | } 40 | 41 | public void OnHideInRaid(MapView map) 42 | { 43 | GameWorldRegisterLootItemPatch.OnRegisterLoot -= OnRegisterLoot; 44 | GameWorldDestroyLootPatch.OnDestroyLoot -= OnDestroyLoot; 45 | } 46 | 47 | public void OnMapChanged(MapView map, MapDef mapDef) 48 | { 49 | _lastMapView = map; 50 | var gameWorld = Singleton.Instance; 51 | 52 | foreach (var itemNetId in _backpackMarkers.Keys.ToList()) 53 | { 54 | TryRemoveMarker(itemNetId); 55 | 56 | // check if item exists as loot item in the world 57 | if (!gameWorld.LootItems.ContainsKey(itemNetId)) 58 | { 59 | continue; 60 | } 61 | 62 | TryAddMarker(map, gameWorld.LootItems.GetByKey(itemNetId)); 63 | } 64 | } 65 | 66 | public void OnRaidEnd(MapView map) 67 | { 68 | GameWorldRegisterLootItemPatch.OnRegisterLoot -= OnRegisterLoot; 69 | GameWorldDestroyLootPatch.OnDestroyLoot -= OnDestroyLoot; 70 | TryRemoveMarkers(); 71 | } 72 | 73 | public void OnDisable(MapView map) 74 | { 75 | GameWorldRegisterLootItemPatch.OnRegisterLoot -= OnRegisterLoot; 76 | GameWorldDestroyLootPatch.OnDestroyLoot -= OnDestroyLoot; 77 | TryRemoveMarkers(); 78 | } 79 | 80 | private void AddThrownBackpacks() 81 | { 82 | var gameWorld = Singleton.Instance; 83 | foreach (var pair in PlayerInventoryThrowItemPatch.ThrownItems) 84 | { 85 | var itemNetId = pair.Key; 86 | var item = pair.Value; 87 | var itemType = ItemViewFactory.GetItemType(item.GetType()); 88 | 89 | // check if item is already in markers or is not a backpack and doesn't not exist as loot item in the world 90 | if (_backpackMarkers.ContainsKey(itemNetId) 91 | || itemType != EItemType.Backpack 92 | || !gameWorld.LootItems.ContainsKey(itemNetId)) 93 | { 94 | continue; 95 | } 96 | 97 | TryAddMarker(_lastMapView, gameWorld.LootItems.GetByKey(itemNetId)); 98 | } 99 | } 100 | 101 | private void OnRegisterLoot(LootItem lootItem) 102 | { 103 | if (lootItem == null || lootItem.Item == null) 104 | { 105 | return; 106 | } 107 | 108 | var itemNetId = lootItem.GetNetId(); 109 | var itemType = ItemViewFactory.GetItemType(lootItem.Item.GetType()); 110 | 111 | if (_backpackMarkers.ContainsKey(itemNetId) 112 | || itemType != EItemType.Backpack 113 | || !PlayerInventoryThrowItemPatch.ThrownItems.ContainsKey(itemNetId)) 114 | { 115 | return; 116 | } 117 | 118 | TryAddMarker(_lastMapView, lootItem); 119 | } 120 | 121 | private void OnDestroyLoot(LootItem lootItem) 122 | { 123 | if (lootItem == null || lootItem.Item == null) 124 | { 125 | return; 126 | } 127 | 128 | TryRemoveMarker(lootItem.GetNetId()); 129 | } 130 | 131 | private void RemoveStaleMarkers() 132 | { 133 | var gameWorld = Singleton.Instance; 134 | foreach (var itemNetId in _backpackMarkers.Keys.ToList()) 135 | { 136 | // check if item is in world 137 | if (gameWorld.LootItems.ContainsKey(itemNetId)) 138 | { 139 | continue; 140 | } 141 | 142 | TryRemoveMarker(itemNetId); 143 | } 144 | } 145 | 146 | private void TryAddMarker(MapView map, LootItem lootItem) 147 | { 148 | if (lootItem == null || lootItem.Item == null) 149 | { 150 | return; 151 | } 152 | 153 | var itemNetId = lootItem.GetNetId(); 154 | if (_backpackMarkers.ContainsKey(itemNetId)) 155 | { 156 | return; 157 | } 158 | 159 | // try adding the marker 160 | var marker = map.AddTransformMarker(lootItem.TrackableTransform, lootItem.Item.ShortName.BSGLocalized(), 161 | _backpackCategory, _backpackColor, _backpackImagePath, _backpackSize); 162 | 163 | _backpackMarkers[itemNetId] = marker; 164 | } 165 | 166 | private void TryRemoveMarker(int itemNetId) 167 | { 168 | if (!_backpackMarkers.ContainsKey(itemNetId)) 169 | { 170 | return; 171 | } 172 | 173 | _backpackMarkers[itemNetId].ContainingMapView.RemoveMapMarker(_backpackMarkers[itemNetId]); 174 | _backpackMarkers.Remove(itemNetId); 175 | } 176 | 177 | private void TryRemoveMarkers() 178 | { 179 | foreach (var itemNetId in _backpackMarkers.Keys.ToList()) 180 | { 181 | TryRemoveMarker(itemNetId); 182 | } 183 | _backpackMarkers.Clear(); 184 | } 185 | 186 | public void OnHideOutOfRaid(MapView map) 187 | { 188 | // do nothing 189 | } 190 | 191 | public void OnShowOutOfRaid(MapView map) 192 | { 193 | // do nothing 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /DynamicMarkers/ExtractMarkerProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Comfort.Common; 4 | using DynamicMaps.Data; 5 | using DynamicMaps.UI.Components; 6 | using DynamicMaps.Utils; 7 | using EFT; 8 | using EFT.Interactive; 9 | using UnityEngine; 10 | 11 | namespace DynamicMaps.DynamicMarkers 12 | { 13 | public class ExtractMarkerProvider : IDynamicMarkerProvider 14 | { 15 | // TODO: move to config 16 | private const string _extractCategory = "Extract"; 17 | private const string _extractImagePath = "Markers/exit.png"; 18 | private static Color _extractDefaultColor = Color.yellow; 19 | private static Color _extractOpenColor = Color.green; 20 | private static Color _extractHasRequirementsColor = Color.yellow; 21 | private static Color _extractClosedColor = Color.red; 22 | // 23 | 24 | private bool _showExtractStatusInRaid = true; 25 | public bool ShowExtractStatusInRaid 26 | { 27 | get 28 | { 29 | return _showExtractStatusInRaid; 30 | } 31 | 32 | set 33 | { 34 | if (_showExtractStatusInRaid == value) 35 | { 36 | return; 37 | } 38 | 39 | _showExtractStatusInRaid = value; 40 | 41 | // force update all statuses 42 | foreach (var extract in _extractMarkers.Keys) 43 | { 44 | UpdateExtractStatus(extract, extract.Status); 45 | } 46 | } 47 | } 48 | 49 | private Dictionary _extractMarkers 50 | = new Dictionary(); 51 | 52 | public void OnShowInRaid(MapView map) 53 | { 54 | // get valid extracts only on first time that this is run in a raid 55 | if (_extractMarkers.Count == 0) 56 | { 57 | AddExtractMarkers(map); 58 | } 59 | 60 | foreach (var extract in _extractMarkers.Keys) 61 | { 62 | // update color based on exfil status 63 | UpdateExtractStatus(extract, extract.Status); 64 | 65 | // subscribe to status changes while map is shown 66 | extract.OnStatusChanged += UpdateExtractStatus; 67 | } 68 | } 69 | 70 | public void OnHideInRaid(MapView map) 71 | { 72 | // unsubscribe from updates while map is hidden 73 | foreach (var extract in _extractMarkers.Keys) 74 | { 75 | extract.OnStatusChanged -= UpdateExtractStatus; 76 | } 77 | } 78 | 79 | public void OnRaidEnd(MapView map) 80 | { 81 | TryRemoveMarkers(); 82 | } 83 | 84 | public void OnMapChanged(MapView map, MapDef mapDef) 85 | { 86 | foreach (var extract in _extractMarkers.Keys.ToList()) 87 | { 88 | TryRemoveMarker(extract); 89 | TryAddMarker(map, extract); 90 | } 91 | } 92 | 93 | public void OnDisable(MapView map) 94 | { 95 | TryRemoveMarkers(); 96 | } 97 | 98 | private void AddExtractMarkers(MapView map) 99 | { 100 | var gameWorld = Singleton.Instance; 101 | var player = GameUtils.GetMainPlayer(); 102 | 103 | IEnumerable extracts; 104 | if (GameUtils.IsScavRaid()) 105 | { 106 | extracts = gameWorld.ExfiltrationController.ScavExfiltrationPoints 107 | .Where(p => p.isActiveAndEnabled && p.InfiltrationMatch(player)) 108 | .Cast(); 109 | } 110 | else 111 | { 112 | extracts = gameWorld.ExfiltrationController.ExfiltrationPoints 113 | .Where(p => p.isActiveAndEnabled && p.InfiltrationMatch(player)); 114 | } 115 | 116 | // add markers, only this single time 117 | foreach (var extract in extracts) 118 | { 119 | TryAddMarker(map, extract); 120 | } 121 | } 122 | 123 | private void TryRemoveMarkers() 124 | { 125 | foreach (var extract in _extractMarkers.Keys.ToList()) 126 | { 127 | TryRemoveMarker(extract); 128 | } 129 | } 130 | 131 | private void UpdateExtractStatus(ExfiltrationPoint extract, EExfiltrationStatus status) 132 | { 133 | if (!_extractMarkers.ContainsKey(extract)) 134 | { 135 | return; 136 | } 137 | 138 | var marker = _extractMarkers[extract]; 139 | if (!_showExtractStatusInRaid) 140 | { 141 | marker.Color = _extractDefaultColor; 142 | return; 143 | } 144 | 145 | switch (extract.Status) 146 | { 147 | case EExfiltrationStatus.NotPresent: 148 | marker.Color = _extractClosedColor; 149 | break; 150 | case EExfiltrationStatus.UncompleteRequirements: 151 | marker.Color = _extractHasRequirementsColor; 152 | return; 153 | default: 154 | marker.Color = _extractOpenColor; 155 | break; 156 | } 157 | } 158 | 159 | private void TryAddMarker(MapView map, ExfiltrationPoint extract) 160 | { 161 | if (_extractMarkers.ContainsKey(extract)) 162 | { 163 | return; 164 | } 165 | 166 | var markerDef = new MapMarkerDef 167 | { 168 | Category = _extractCategory, 169 | ImagePath = _extractImagePath, 170 | Text = extract.Settings.Name.BSGLocalized(), 171 | Position = MathUtils.ConvertToMapPosition(extract.transform) 172 | }; 173 | 174 | var marker = map.AddMapMarker(markerDef); 175 | _extractMarkers[extract] = marker; 176 | 177 | UpdateExtractStatus(extract, extract.Status); 178 | } 179 | 180 | private void TryRemoveMarker(ExfiltrationPoint extract) 181 | { 182 | if (!_extractMarkers.ContainsKey(extract)) 183 | { 184 | return; 185 | } 186 | 187 | extract.OnStatusChanged -= UpdateExtractStatus; 188 | 189 | _extractMarkers[extract].ContainingMapView.RemoveMapMarker(_extractMarkers[extract]); 190 | _extractMarkers.Remove(extract); 191 | } 192 | 193 | public void OnShowOutOfRaid(MapView map) 194 | { 195 | // do nothing 196 | } 197 | 198 | public void OnHideOutOfRaid(MapView map) 199 | { 200 | // do nothing 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /DynamicMarkers/IDynamicMarkerProvider.cs: -------------------------------------------------------------------------------- 1 | using DynamicMaps.Data; 2 | using DynamicMaps.UI.Components; 3 | 4 | namespace DynamicMaps.DynamicMarkers 5 | { 6 | public interface IDynamicMarkerProvider 7 | { 8 | void OnShowOutOfRaid(MapView map); 9 | void OnHideOutOfRaid(MapView map); 10 | 11 | void OnShowInRaid(MapView map); 12 | void OnHideInRaid(MapView map); 13 | 14 | void OnRaidEnd(MapView map); 15 | 16 | void OnMapChanged(MapView map, MapDef mapDef); 17 | 18 | void OnDisable(MapView map); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /DynamicMarkers/LockedDoorMarkerMutator.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using DynamicMaps.Data; 4 | using DynamicMaps.UI.Components; 5 | using DynamicMaps.Utils; 6 | using UnityEngine; 7 | 8 | namespace DynamicMaps.DynamicMarkers 9 | { 10 | public class LockedDoorMarkerMutator : IDynamicMarkerProvider 11 | { 12 | // FIXME: move to configuration somehow 13 | private static string doorWithKeyPath = Path.Combine(Plugin.Path, "Markers/door_with_key.png"); 14 | private static string doorWithLockPath = Path.Combine(Plugin.Path, "Markers/door_with_lock.png"); 15 | 16 | public void OnShowInRaid(MapView map) 17 | { 18 | var player = GameUtils.GetMainPlayer(); 19 | var markers = map.GetMapMarkersByCategory("LockedDoor"); 20 | 21 | foreach (var marker in markers) 22 | { 23 | if (string.IsNullOrWhiteSpace(marker.AssociatedItemId)) 24 | { 25 | continue; 26 | } 27 | 28 | // TODO: GetAllItems is a BSG extension method under GClass 29 | var hasKey = player.Inventory.Equipment.GetAllItems().Any(i => i.TemplateId == marker.AssociatedItemId); 30 | 31 | marker.Image.sprite = hasKey 32 | ? TextureUtils.GetOrLoadCachedSprite(doorWithKeyPath) 33 | : TextureUtils.GetOrLoadCachedSprite(doorWithLockPath); 34 | 35 | marker.Color = hasKey 36 | ? Color.green 37 | : Color.red; 38 | } 39 | } 40 | 41 | public void OnShowOutOfRaid(MapView map) 42 | { 43 | var profile = GameUtils.GetPlayerProfile(); 44 | var markers = map.GetMapMarkersByCategory("LockedDoor"); 45 | 46 | foreach (var marker in markers) 47 | { 48 | if (string.IsNullOrWhiteSpace(marker.AssociatedItemId)) 49 | { 50 | continue; 51 | } 52 | 53 | // TODO: GetAllItems is a BSG extension method under GClass 54 | var keyInStash = profile.Inventory.Stash.GetAllItems().Any(i => i.TemplateId == marker.AssociatedItemId); 55 | var keyInEquipment = profile.Inventory.Equipment.GetAllItems().Any(i => i.TemplateId == marker.AssociatedItemId); 56 | 57 | // change icon 58 | marker.Image.sprite = (keyInStash || keyInEquipment) 59 | ? TextureUtils.GetOrLoadCachedSprite(doorWithKeyPath) 60 | : TextureUtils.GetOrLoadCachedSprite(doorWithLockPath); 61 | 62 | // change color 63 | marker.Color = keyInEquipment 64 | ? Color.green 65 | : keyInStash 66 | ? Color.yellow 67 | : Color.red; 68 | } 69 | } 70 | 71 | public void OnMapChanged(MapView map, MapDef mapDef) 72 | { 73 | if (GameUtils.IsInRaid()) 74 | { 75 | OnShowInRaid(map); 76 | } 77 | else 78 | { 79 | OnShowOutOfRaid(map); 80 | } 81 | } 82 | 83 | public void OnDisable(MapView map) 84 | { 85 | var markers = map.GetMapMarkersByCategory("LockedDoor"); 86 | 87 | // replace all markers with yellow and with lock 88 | foreach (var marker in markers) 89 | { 90 | if (string.IsNullOrWhiteSpace(marker.AssociatedItemId)) 91 | { 92 | continue; 93 | } 94 | 95 | marker.Image.sprite = TextureUtils.GetOrLoadCachedSprite(doorWithLockPath); 96 | marker.Color = Color.yellow; 97 | } 98 | } 99 | 100 | public void OnRaidEnd(MapView map) 101 | { 102 | // do nothing 103 | } 104 | 105 | public void OnHideInRaid(MapView map) 106 | { 107 | // do nothing 108 | } 109 | 110 | public void OnHideOutOfRaid(MapView map) 111 | { 112 | // do nothing 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /DynamicMarkers/PlayerMarkerProvider.cs: -------------------------------------------------------------------------------- 1 | using DynamicMaps.Data; 2 | using DynamicMaps.UI.Components; 3 | using DynamicMaps.Utils; 4 | using UnityEngine; 5 | 6 | namespace DynamicMaps.DynamicMarkers 7 | { 8 | public class PlayerMarkerProvider : IDynamicMarkerProvider 9 | { 10 | // TODO: move to config 11 | private const string _playerCategory = "Main Player"; 12 | private const string _playerImagePath = "Markers/arrow.png"; 13 | private static Color _playerColor = Color.green; 14 | // 15 | 16 | private PlayerMapMarker _playerMarker; 17 | 18 | public void OnShowInRaid(MapView map) 19 | { 20 | TryAddMarker(map); 21 | } 22 | 23 | public void OnRaidEnd(MapView map) 24 | { 25 | TryRemoveMarker(); 26 | } 27 | 28 | public void OnMapChanged(MapView map, MapDef mapDef) 29 | { 30 | TryRemoveMarker(); 31 | 32 | if (GameUtils.IsInRaid()) 33 | { 34 | TryAddMarker(map); 35 | } 36 | } 37 | 38 | public void OnDisable(MapView map) 39 | { 40 | TryRemoveMarker(); 41 | } 42 | 43 | private void TryAddMarker(MapView map) 44 | { 45 | if (_playerMarker != null) 46 | { 47 | return; 48 | } 49 | 50 | var player = GameUtils.GetMainPlayer(); 51 | if (player == null) 52 | { 53 | return; 54 | } 55 | 56 | // try adding the marker 57 | _playerMarker = map.AddPlayerMarker(player, _playerCategory, _playerColor, _playerImagePath); 58 | } 59 | 60 | private void TryRemoveMarker() 61 | { 62 | if (_playerMarker == null) 63 | { 64 | return; 65 | } 66 | 67 | _playerMarker.ContainingMapView.RemoveMapMarker(_playerMarker); 68 | _playerMarker = null; 69 | } 70 | 71 | public void OnHideInRaid(MapView map) 72 | { 73 | // do nothing 74 | } 75 | 76 | public void OnShowOutOfRaid(MapView map) 77 | { 78 | // do nothing 79 | } 80 | 81 | public void OnHideOutOfRaid(MapView map) 82 | { 83 | // do nothing 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /DynamicMarkers/QuestMarkerProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using DynamicMaps.Data; 3 | using DynamicMaps.DynamicMarkers; 4 | using DynamicMaps.UI.Components; 5 | using DynamicMaps.Utils; 6 | 7 | namespace DynamicMaps 8 | { 9 | public class QuestMarkerProvider : IDynamicMarkerProvider 10 | { 11 | private List _questMarkers = new List(); 12 | 13 | public void OnShowInRaid(MapView map) 14 | { 15 | if (GameUtils.IsScavRaid()) 16 | { 17 | return; 18 | } 19 | 20 | AddQuestObjectiveMarkers(map); 21 | } 22 | 23 | public void OnHideInRaid(MapView map) 24 | { 25 | // TODO: don't just be lazy and try to update markers 26 | TryRemoveMarkers(); 27 | } 28 | 29 | public void OnMapChanged(MapView map, MapDef mapDef) 30 | { 31 | if (!GameUtils.IsInRaid()) 32 | { 33 | return; 34 | } 35 | 36 | TryRemoveMarkers(); 37 | AddQuestObjectiveMarkers(map); 38 | } 39 | 40 | public void OnRaidEnd(MapView map) 41 | { 42 | QuestUtils.DiscardQuestData(); 43 | TryRemoveMarkers(); 44 | } 45 | 46 | public void OnDisable(MapView map) 47 | { 48 | TryRemoveMarkers(); 49 | } 50 | 51 | private void AddQuestObjectiveMarkers(MapView map) 52 | { 53 | QuestUtils.TryCaptureQuestData(); 54 | 55 | var player = GameUtils.GetMainPlayer(); 56 | 57 | var markerDefs = QuestUtils.GetMarkerDefsForPlayer(player); 58 | foreach (var markerDef in markerDefs) 59 | { 60 | var marker = map.AddMapMarker(markerDef); 61 | _questMarkers.Add(marker); 62 | } 63 | } 64 | 65 | private void TryRemoveMarkers() 66 | { 67 | foreach (var marker in _questMarkers) 68 | { 69 | marker.ContainingMapView.RemoveMapMarker(marker); 70 | } 71 | _questMarkers.Clear(); 72 | } 73 | 74 | public void OnShowOutOfRaid(MapView map) 75 | { 76 | // do nothing 77 | } 78 | 79 | public void OnHideOutOfRaid(MapView map) 80 | { 81 | // do nothing 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /FEATURE_WISHLIST.md: -------------------------------------------------------------------------------- 1 | ## Potential Features 2 | 3 | ### Configuration 4 | 5 | - Configure colors/icons for dynamic markers 6 | 7 | ### Marker/Label Filtering 8 | 9 | - Be able to toggle on/off categories of marker and labels 10 | 11 | ### Heatmaps 12 | 13 | - Heatmap for enemies and loot would be cool 14 | 15 | ### Map Markers 16 | 17 | - Add lootable/loose loot dynamic markers? 18 | - Add temporary marker (left click?) 19 | - Add player created markers (right click menu?) 20 | 21 | #### Tooltips 22 | 23 | - Locked door: what key is needed 24 | - Quests: the condition's description 25 | - Bot debug markers: additional bot debug information 26 | - Extracts: if cannot use immediately, what the hint is to enable it 27 | - Switches/buttons: what the button does 28 | 29 | #### Better and Clearer Icons 30 | 31 | - Add button icon for interactable 32 | - Add console icon for interactable 33 | - Add keycard door icon, locked/with card 34 | - Add car trunk icon, locked/with key 35 | - Add car door icon, locked/with key 36 | - Add safe icon, locked/with key 37 | 38 | ### Maps 39 | 40 | - Could load higher resolution from asset bundle, as that uses different pipeline, and wouldn't be so slow 41 | - Could use additional layers on the same level to overlay higher resolution buildings 42 | - Update for more detail on building interiors 43 | 44 | ### Overlay 45 | 46 | - Add option to select a marker and have an overlay UI pointing to the marker 47 | 48 | ### Immersion and Difficulty 49 | 50 | - Tarkov is an immersive game and mod should support players that want to preserve that 51 | - Add additional options that support that: 52 | - Only show player position if compass equipped 53 | - Only reveal locked doors that the character has interacted with 54 | - Only allow map usage if player has map equipped 55 | -------------------------------------------------------------------------------- /KNOWN_ISSUES.md: -------------------------------------------------------------------------------- 1 | ## Known Issues 2 | 3 | - Maps are relatively low resolution due to long load times and concerns about GPU memory usage 4 | - Maps and map labels could be better aligned 5 | - Out of raid maps do not contain any quest markers 6 | - Car doors do not use the correct icon 7 | 8 | - Included Interchange map does not contain a lot of detail 9 | - Included Labs map is different overall style 10 | - Many of the included maps do not contain every building's multiple floors 11 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Michael P. Starkweather 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Patches/AirdropBoxPatches.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using SPT.Custom.Airdrops; 5 | using SPT.Reflection.Patching; 6 | using HarmonyLib; 7 | 8 | namespace DynamicMaps.Patches 9 | { 10 | internal class AirdropBoxOnBoxLandPatch : ModulePatch 11 | { 12 | internal static event Action OnAirdropLanded; 13 | internal static List Airdrops = new List(); 14 | 15 | private bool _hasRegisteredEvents = false; 16 | 17 | protected override MethodBase GetTargetMethod() 18 | { 19 | if (!_hasRegisteredEvents) 20 | { 21 | GameWorldOnDestroyPatch.OnRaidEnd += OnRaidEnd; 22 | _hasRegisteredEvents = true; 23 | } 24 | 25 | // thanks to TechHappy for the breadcrumb of what method to patch 26 | return AccessTools.Method(typeof(AirdropBox), "OnBoxLand"); 27 | } 28 | 29 | [PatchPostfix] 30 | public static void PatchPostfix(AirdropBox __instance) 31 | { 32 | Airdrops.Add(__instance); 33 | OnAirdropLanded?.Invoke(__instance); 34 | } 35 | 36 | internal static void OnRaidEnd() 37 | { 38 | Airdrops.Clear(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Patches/BattleUIScreenPatches.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using SPT.Reflection.Patching; 4 | using EFT; 5 | using EFT.UI; 6 | using HarmonyLib; 7 | 8 | namespace DynamicMaps.Patches 9 | { 10 | internal class BattleUIScreenShowPatch : ModulePatch 11 | { 12 | protected override MethodBase GetTargetMethod() 13 | { 14 | return AccessTools.Method(typeof(EftBattleUIScreen), 15 | nameof(EftBattleUIScreen.Show), 16 | new Type[] { typeof(GamePlayerOwner) }); 17 | } 18 | 19 | [PatchPostfix] 20 | public static void PatchPostfix(EftBattleUIScreen __instance) 21 | { 22 | Plugin.Instance.TryAttachToBattleUIScreen(__instance); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Patches/CommonUIPatches.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using SPT.Reflection.Patching; 3 | using EFT.UI; 4 | using EFT.UI.Map; 5 | using HarmonyLib; 6 | 7 | namespace DynamicMaps.Patches 8 | { 9 | internal class CommonUIAwakePatch : ModulePatch 10 | { 11 | protected override MethodBase GetTargetMethod() 12 | { 13 | return AccessTools.Method(typeof(CommonUI), nameof(CommonUI.Awake)); 14 | } 15 | 16 | [PatchPostfix] 17 | public static void PatchPostfix(CommonUI __instance) 18 | { 19 | var mapScreen = Traverse.Create(__instance.InventoryScreen).Field("_mapScreen").GetValue(); 20 | 21 | Plugin.Instance.TryAttachToMapScreen(mapScreen); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Patches/GameWorldPatches.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using SPT.Reflection.Patching; 5 | using EFT; 6 | using EFT.Interactive; 7 | using HarmonyLib; 8 | 9 | namespace DynamicMaps.Patches 10 | { 11 | internal class GameWorldOnDestroyPatch : ModulePatch 12 | { 13 | internal static event Action OnRaidEnd; 14 | 15 | protected override MethodBase GetTargetMethod() 16 | { 17 | return AccessTools.Method(typeof(GameWorld), nameof(GameWorld.OnDestroy)); 18 | } 19 | 20 | [PatchPrefix] 21 | public static void PatchPrefix() 22 | { 23 | try 24 | { 25 | OnRaidEnd?.Invoke(); 26 | } 27 | catch(Exception e) 28 | { 29 | Plugin.Log.LogError($"Caught error while doing end of raid calculations"); 30 | Plugin.Log.LogError($"{e.Message}"); 31 | Plugin.Log.LogError($"{e.StackTrace}"); 32 | } 33 | } 34 | } 35 | 36 | internal class GameWorldUnregisterPlayerPatch : ModulePatch 37 | { 38 | internal static event Action OnUnregisterPlayer; 39 | 40 | protected override MethodBase GetTargetMethod() 41 | { 42 | return AccessTools.Method(typeof(GameWorld), nameof(GameWorld.UnregisterPlayer)); 43 | } 44 | 45 | [PatchPostfix] 46 | public static void PatchPostfix(IPlayer iPlayer) 47 | { 48 | OnUnregisterPlayer?.Invoke(iPlayer); 49 | } 50 | } 51 | 52 | internal class GameWorldRegisterLootItemPatch : ModulePatch 53 | { 54 | internal static event Action OnRegisterLoot; 55 | 56 | protected override MethodBase GetTargetMethod() 57 | { 58 | return typeof(GameWorld).GetMethod("RegisterLoot").MakeGenericMethod(typeof(LootItem)); 59 | } 60 | 61 | [PatchPostfix] 62 | public static void PatchPostfix(LootItem loot) 63 | { 64 | OnRegisterLoot?.Invoke(loot); 65 | } 66 | } 67 | 68 | internal class GameWorldDestroyLootPatch : ModulePatch 69 | { 70 | internal static event Action OnDestroyLoot; 71 | 72 | protected override MethodBase GetTargetMethod() 73 | { 74 | return typeof(GameWorld).GetMethods(BindingFlags.Public | BindingFlags.Instance) 75 | .First(m => m.Name == "DestroyLoot" && m.GetParameters().FirstOrDefault(p => p.Name == "loot") != null); 76 | } 77 | 78 | [PatchPrefix] 79 | public static void PatchPrefix(Object loot) 80 | { 81 | try 82 | { 83 | if (loot is LootItem lootItem) 84 | { 85 | OnDestroyLoot?.Invoke(lootItem); 86 | } 87 | } 88 | catch (Exception e) 89 | { 90 | Plugin.Log.LogError($"Caught error while running DestroyLoot patch"); 91 | Plugin.Log.LogError($"{e.Message}"); 92 | Plugin.Log.LogError($"{e.StackTrace}"); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Patches/MapScreenPatches.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using SPT.Reflection.Patching; 4 | using DynamicMaps.Config; 5 | using EFT.UI.Map; 6 | using HarmonyLib; 7 | 8 | namespace DynamicMaps.Patches 9 | { 10 | internal class MapScreenShowPatch : ModulePatch 11 | { 12 | protected override MethodBase GetTargetMethod() 13 | { 14 | return AccessTools.Method(typeof(MapScreen), nameof(MapScreen.Show)); 15 | } 16 | 17 | [PatchPrefix] 18 | public static bool PatchPrefix() 19 | { 20 | try 21 | { 22 | if (!Settings.ReplaceMapScreen.Value) 23 | { 24 | // mod is disabled 25 | Plugin.Instance.Map?.OnMapScreenClose(); 26 | return true; 27 | } 28 | 29 | // show instead 30 | Plugin.Instance.Map?.OnMapScreenShow(); 31 | return false; 32 | } 33 | catch(Exception e) 34 | { 35 | Plugin.Log.LogError($"Caught error while trying to show map"); 36 | Plugin.Log.LogError($"{e.Message}"); 37 | Plugin.Log.LogError($"{e.StackTrace}"); 38 | 39 | return true; 40 | } 41 | } 42 | } 43 | 44 | internal class MapScreenClosePatch : ModulePatch 45 | { 46 | protected override MethodBase GetTargetMethod() 47 | { 48 | return AccessTools.Method(typeof(MapScreen), nameof(MapScreen.Close)); 49 | } 50 | 51 | [PatchPrefix] 52 | public static bool PatchPrefix() 53 | { 54 | try 55 | { 56 | if (!Settings.ReplaceMapScreen.Value) 57 | { 58 | // mod is disabled 59 | return true; 60 | } 61 | 62 | // close instead 63 | Plugin.Instance.Map?.OnMapScreenClose(); 64 | return false; 65 | } 66 | catch(Exception e) 67 | { 68 | Plugin.Log.LogError($"Caught error while trying to close map"); 69 | Plugin.Log.LogError($"{e.Message}"); 70 | Plugin.Log.LogError($"{e.StackTrace}"); 71 | 72 | return true; 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Patches/PlayerPatches.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using SPT.Reflection.Patching; 5 | using DynamicMaps.Utils; 6 | using EFT; 7 | using EFT.Interactive; 8 | using EFT.InventoryLogic; 9 | using HarmonyLib; 10 | 11 | namespace DynamicMaps.Patches 12 | { 13 | internal class PlayerOnDeadPatch : ModulePatch 14 | { 15 | internal static event Action OnDead; 16 | 17 | protected override MethodBase GetTargetMethod() 18 | { 19 | return AccessTools.Method(typeof(Player), nameof(Player.OnDead)); 20 | } 21 | 22 | [PatchPostfix] 23 | public static void PatchPostfix(Player __instance) 24 | { 25 | OnDead?.Invoke(__instance); 26 | } 27 | } 28 | 29 | internal class PlayerInventoryThrowItemPatch : ModulePatch 30 | { 31 | private static FieldInfo _playerInventoryControllerPlayerField = AccessTools.Field(typeof(Player.PlayerInventoryController), "player_0"); 32 | 33 | internal static event Action OnThrowItem; 34 | internal static Dictionary ThrownItems = new Dictionary(); 35 | 36 | private bool _hasRegisteredEvents = false; 37 | 38 | protected override MethodBase GetTargetMethod() 39 | { 40 | if (!_hasRegisteredEvents) 41 | { 42 | GameWorldOnDestroyPatch.OnRaidEnd += OnRaidEnd; 43 | GameWorldDestroyLootPatch.OnDestroyLoot += OnDestroyLoot; 44 | _hasRegisteredEvents = true; 45 | } 46 | 47 | return AccessTools.Method(typeof(Player.PlayerInventoryController), 48 | nameof(Player.PlayerInventoryController.ThrowItem)); 49 | } 50 | 51 | [PatchPostfix] 52 | public static void PatchPostfix(Player.PlayerInventoryController __instance, Item item) 53 | { 54 | // only look at main player's dropped items 55 | var player = _playerInventoryControllerPlayerField.GetValue(__instance) as Player; 56 | if (player == null || player != GameUtils.GetMainPlayer()) 57 | { 58 | return; 59 | } 60 | 61 | var itemNetId = item.Id.GetHashCode(); 62 | ThrownItems[itemNetId] = item; 63 | OnThrowItem?.Invoke(itemNetId, item); 64 | } 65 | 66 | internal static void OnDestroyLoot(LootItem lootItem) 67 | { 68 | if (lootItem == null || lootItem.Item == null) 69 | { 70 | return; 71 | } 72 | 73 | ThrownItems.Remove(lootItem.GetNetId()); 74 | } 75 | 76 | internal static void OnRaidEnd() 77 | { 78 | ThrownItems.Clear(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Plugin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using BepInEx; 4 | using BepInEx.Logging; 5 | using Comfort.Common; 6 | using DrakiaXYZ.VersionChecker; 7 | using DynamicMaps.Config; 8 | using DynamicMaps.Patches; 9 | using DynamicMaps.UI; 10 | using EFT.UI; 11 | using EFT.UI.Map; 12 | 13 | namespace DynamicMaps 14 | { 15 | // the version number here is generated on build and may have a warning if not yet built 16 | [BepInPlugin("com.mpstark.DynamicMaps", "DynamicMaps", BuildInfo.Version)] 17 | [BepInDependency("com.SPT.custom")] 18 | public class Plugin : BaseUnityPlugin 19 | { 20 | public const int TarkovVersion = 30626; 21 | public static Plugin Instance; 22 | public static ManualLogSource Log => Instance.Logger; 23 | public static string Path = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 24 | 25 | public ModdedMapScreen Map; 26 | 27 | internal void Awake() 28 | { 29 | if (!VersionChecker.CheckEftVersion(Logger, Info, Config)) 30 | { 31 | throw new Exception("Invalid EFT Version"); 32 | } 33 | 34 | Settings.Init(Config); 35 | Config.SettingChanged += (x, y) => Map?.ReadConfig(); 36 | 37 | Instance = this; 38 | 39 | // patches 40 | new BattleUIScreenShowPatch().Enable(); 41 | new CommonUIAwakePatch().Enable(); 42 | new MapScreenShowPatch().Enable(); 43 | new MapScreenClosePatch().Enable(); 44 | new GameWorldOnDestroyPatch().Enable(); 45 | new GameWorldUnregisterPlayerPatch().Enable(); 46 | new GameWorldRegisterLootItemPatch().Enable(); 47 | new GameWorldDestroyLootPatch().Enable(); 48 | new AirdropBoxOnBoxLandPatch().Enable(); 49 | new PlayerOnDeadPatch().Enable(); 50 | new PlayerInventoryThrowItemPatch().Enable(); 51 | } 52 | 53 | /// 54 | /// Attach to the map screen 55 | /// 56 | internal void TryAttachToMapScreen(MapScreen mapScreen) 57 | { 58 | if (Map != null) 59 | { 60 | return; 61 | } 62 | 63 | Log.LogInfo("Trying to attach to MapScreen"); 64 | 65 | // attach to common UI first to call awake and set things up, then attach to sleeping map screen 66 | Map = ModdedMapScreen.Create(Singleton.Instance.gameObject); 67 | Map.transform.SetParent(mapScreen.transform); 68 | } 69 | 70 | /// 71 | /// Attach the peek component 72 | /// 73 | internal void TryAttachToBattleUIScreen(EftBattleUIScreen battleUI) 74 | { 75 | if (Map == null) 76 | { 77 | return; 78 | } 79 | 80 | Map.TryAddPeekComponent(battleUI); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About Mod 2 | 3 | An SPT mod that adds a custom in-game map viewer in the place of the BSG map screen. Includes pre-configured maps made by [TarkovData](https://github.com/TarkovTracker/tarkovdata/) and [TarkovDev](https://github.com/the-hideout/tarkov-dev). 4 | 5 | #### Full screen out of raid maps with selector 6 | ![Out of Raid Map with selector](Screenshots/out_of_raid_map.png) 7 | 8 | #### Full screen in-raid with player marker 9 | ![In raid with player marker](Screenshots/in_raid_map.png) 10 | 11 | #### Dynamic Locks 12 | ![Dynamic Locks](Screenshots/dynamic_locks.png) 13 | 14 | #### Quest Indicators 15 | ![Quest Indicators](Screenshots/quest_markers.png) 16 | 17 | #### BTR Marker 18 | ![BTR Marker](Screenshots/btr_marker.png) 19 | 20 | #### Airdrop Marker 21 | ![Airdrop Marker](Screenshots/airdrop_marker.png) 22 | 23 | #### Corpse Markers 24 | ![Corpse Markers](Screenshots/corpse_markers.png) 25 | 26 | ### Features 27 | 28 | - Map organized in stacked layers / levels 29 | - Text map labels supported overlaid on map 30 | - Automatic selection of layer based on player position (configurable) 31 | - Manual level control available on left of map screen (as well as shift-scroll and configurable hotkeys) 32 | - Automatic min/max zoom based on size of map 33 | - Support for coordinate rotation, since BSG decided to make north different direction on many of the maps 34 | - Drag-based map pan and mousewheel-based map zoom controls; additional hotkeys for map control available (configurable) 35 | - Peek at map hotkey (configurable) 36 | - Icon-based map markers placed both statically and dynamically. Currently: 37 | - In-raid dynamic player icon 38 | - In-raid dynamic current extracts for player 39 | - In-raid dynamic quest icons (loosely based on [Prop's GTFO](https://github.com/dvize/GTFO)) 40 | - In-raid dynamic player-dropped backpacks 41 | - In-raid dynamic BTR icon (with icon by Kuromi, see [`marker_credits.txt`](Resources/Markers/marker_credits.txt) for more info) 42 | - In-raid dynamic airdrop icons (generated when airdrop lands) 43 | - In-raid dynamic markers for corpses 44 | - In-raid dynamic other players/bots icons 45 | - Friendly players will only show if using another mod that adds multiplayer or adds friendly bots (not sure if that exists) 46 | - Enemy players, bosses, and scavs off by default, intended for mostly debug 47 | - Static markers for all extracts for all maps out-of-raid 48 | - Statically-loaded locked door with dynamic icon and color based on key status 49 | - Out-of-raid, green with key means player has it in inventory, yellow with key means key in stash, red with lock otherwise 50 | - In-raid, green with key means player has the key, red with lock means player doesn't have key 51 | - Static markers for switches and levers 52 | 53 | See [`KNOWN_ISSUES.md`](KNOWN_ISSUES.md) for known current issues and [`FEATURE_WISHLIST.md`](FEATURE_WISHLIST.md) for a list of things that I would like to work on in the future. 54 | 55 | ## Configuration 56 | 57 | ### General 58 | 59 | - **Replace Map Screen**: If the map should replace the BSG default map screen, requires swapping away from modded map to refresh 60 | - **Center on Player Hotkey**: Pressed while the map is open, centers the player 61 | - **Move Map * Hotkey**: Hotkey to move the map * 62 | - **Move Map Hotkey Speed**: How fast the map should move, units are map percent per second 63 | - **Change Map Level * Hotkey**: Hotkey to move the map level * (shift-scroll also does this in map screen) 64 | - **Zoom Map * Hotkey**: Hotkey to zoom the map * (scroll also does this in map screen) 65 | - **Zoom Map Hotkey Speed**: Zoom Map Hotkey Speed 66 | - **Dump Info Hotkey**: Pressed while the map is open, dumps json MarkerDefs for extracts, loot, and switches into root of plugin folder (only shows in advanced config mode) 67 | 68 | ### Dynamic Markers 69 | 70 | - **Show Player Marker**: If the player marker should be shown in raid 71 | - **Show Friendly Player Markers**: If friendly player markers should be shown 72 | - **Show Enemy Player Markers**: If enemy player markers should be shown (generally for debug) 73 | - **Show Scav Markers**: If enemy scav markers should be shown (generally for debug) 74 | - **Show Boss Markers**: If enemy boss markers should be shown 75 | - **Show Locked Door Status**: If locked door markers should be updated with status based on key acquisition 76 | - **Show Quests In Raid**: If quests should be shown in raid 77 | - **Show Extracts In Raid**: If extracts should be shown in raid 78 | - **Show Extracts Status In Raid**: If extracts should be colored according to their status in raid 79 | - **Show Dropped Backpack In Raid**: If the player's dropped backpacks (not anyone elses) should be shown in raid 80 | - **Show BTR In Raid**: If the BTR should be shown in raid 81 | - **Show Airdrop In Raid**: If airdrops should be shown in raid when they land 82 | - **Show Friendly Corpses In Raid**: If friendly corpses should be shown in raid 83 | - **Show Player-killed Corpses In Raid**: If corpses killed by the player should be shown in raid, killed bosses will be shown in another color 84 | - **Show Friendly-killed Corpses In Raid**: If corpses killed by friendly players should be shown in raid, killed bosses will be shown in another color 85 | - **Show Boss Corpses In Raid**: If boss corpses (other than ones killed by the player) should be shown in raid 86 | - **Show Other Corpses In Raid**: If corpses (other than friendly ones or ones killed by the player) should be shown in raid 87 | 88 | ### In-Raid 89 | 90 | - **Auto Select Level**: If the level should be automatically selected based on the players position in raid 91 | - **Auto Center On Player Marker**: If the player marker should be centered when showing the map in raid 92 | - **Reset Zoom On Center**: If the zoom level should be reset each time that the map is opened while in raid 93 | - **Centering On Player Zoom Level**: What zoom level should be used as while centering on the player (0 is fully zoomed out, and 1 is fully zoomed in) 94 | - **Peek at Map Shortcut**: The keyboard shortcut to peek at the map 95 | - **Hold for Peek**: If the shortcut should be held to keep it open. If disabled, button toggles 96 | 97 | ## Installation 98 | 99 | [Releases are here](https://github.com/mpstark/SPT-DynamicMaps/releases). Open zip file and drag `BepInEx` folder into root of your SPT-AKI install. 100 | 101 | ## License 102 | 103 | Distributed under the MIT license. See [`LICENSE.txt`](LICENSE.txt) for more details. 104 | 105 | ## Acknowledgments 106 | 107 | - [CJ](https://github.com/CJ-SPT) for letting me hack on [StashSearch](https://github.com/CJ-SPT/StashSearch) as my first SPT-AKI modding experience 108 | - [DrakiaXYZ](https://github.com/DrakiaXYZ) for having multiple great mods to look at for examples 109 | - [Arys](https://github.com/Nympfonic) for being awesome 110 | - [Kuromi](https://github.com/schkuromi/) for the BTR icon and constant support 111 | - Multiple people in the SPT Discord for suggestions and encouragement 112 | -------------------------------------------------------------------------------- /Resources/Maps/Customs_TarkovDev/Layers/SVG/Customs-Underground_Level.svg: -------------------------------------------------------------------------------- 1 | 2 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /Resources/Maps/Customs_TarkovDev/Layers/customs_level_-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Customs_TarkovDev/Layers/customs_level_-1.png -------------------------------------------------------------------------------- /Resources/Maps/Customs_TarkovDev/Layers/customs_level_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Customs_TarkovDev/Layers/customs_level_0.png -------------------------------------------------------------------------------- /Resources/Maps/Customs_TarkovDev/Layers/customs_level_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Customs_TarkovDev/Layers/customs_level_1.png -------------------------------------------------------------------------------- /Resources/Maps/Customs_TarkovDev/Layers/customs_level_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Customs_TarkovDev/Layers/customs_level_2.png -------------------------------------------------------------------------------- /Resources/Maps/Factory_TarkovDev/Factory_TarkovDev.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "DisplayName": "Factory (TarkovDev)", 3 | "Author": "Tarkov.dev", 4 | "AuthorLink": "https://tarkov.dev", 5 | "MapInternalNames": [ "factory4_day", "factory4_night" ], 6 | "CoordinateRotation": 90, 7 | "Bounds": { 8 | "Min": {"x": -65, "y": -64.5}, 9 | "Max": {"x": 77.6, "y": 67.2} 10 | }, 11 | "DefaultLevel": 0, 12 | "Layers": { 13 | "Tunnels": { 14 | "Level": -1, 15 | "ImagePath": "Maps/Factory_TarkovDev/Layers/factory_layer_-1.png", 16 | "ImageBounds": { 17 | "Min": {"x": -65, "y": -64.5}, 18 | "Max": {"x": 77.6, "y": 67.2} 19 | }, 20 | "GameBounds": [ 21 | { 22 | "Min": {"x": -65, "y": -64.5, "z": -100}, 23 | "Max": {"x": 77.6, "y": 67.2, "z": -1} 24 | } 25 | ] 26 | }, 27 | "Ground Floor": { 28 | "Level": 0, 29 | "ImagePath": "Maps/Factory_TarkovDev/Layers/factory_layer_0.png", 30 | "ImageBounds": { 31 | "Min": {"x": -65, "y": -64.5}, 32 | "Max": {"x": 77.6, "y": 67.2} 33 | }, 34 | "GameBounds": [ 35 | { 36 | "Min": {"x": -65, "y": -64.5, "z": -1}, 37 | "Max": {"x": 77.6, "y": 67.2, "z": 3} 38 | } 39 | ] 40 | }, 41 | "2nd Floor": { 42 | "Level": 1, 43 | "ImagePath": "Maps/Factory_TarkovDev/Layers/factory_layer_1.png", 44 | "ImageBounds": { 45 | "Min": {"x": -65, "y": -64.5}, 46 | "Max": {"x": 77.6, "y": 67.2} 47 | }, 48 | "GameBounds": [ 49 | { 50 | "Min": {"x": -65, "y": -64.5, "z": 3}, 51 | "Max": {"x": 77.6, "y": 67.2, "z": 6} 52 | } 53 | ] 54 | }, 55 | "3rd Floor": { 56 | "Level": 2, 57 | "ImagePath": "Maps/Factory_TarkovDev/Layers/factory_layer_2.png", 58 | "ImageBounds": { 59 | "Min": {"x": -65, "y": -64.5}, 60 | "Max": {"x": 77.6, "y": 67.2} 61 | }, 62 | "GameBounds": [ 63 | { 64 | "Min": {"x": -65, "y": -64.5, "z": 6}, 65 | "Max": {"x": 77.6, "y": 67.2, "z": 100} 66 | } 67 | ] 68 | } 69 | }, 70 | "Labels": [ 71 | { 72 | "Text": "Office Building", 73 | "Position": {"x": 21, "y": 39, "z": 0}, 74 | "FontSize": 14 75 | }, 76 | { 77 | "Text": "Med Tent", 78 | "Position": {"x": -17.5, "y": -28, "z": 0}, 79 | "FontSize": 14 80 | }, 81 | { 82 | "Text": "Silos", 83 | "Position": {"x": 4.5, "y": 10.5, "z": 0}, 84 | "FontSize": 14 85 | }, 86 | { 87 | "Text": "Heli Crash", 88 | "Position": {"x": 30, "y": -8.5, "z": 0}, 89 | "FontSize": 14 90 | }, 91 | { 92 | "Text": "Scav Bunker", 93 | "Position": {"x": -20.5, "y": 23, "z": 0}, 94 | "FontSize": 14 95 | }, 96 | { 97 | "Text": "Blue Containers", 98 | "Position": {"x": -18, "y": 50, "z": 0}, 99 | "FontSize": 14 100 | }, 101 | { 102 | "Text": "Glass Hall", 103 | "Position": {"x": 69.3, "y": -20, "z": 0}, 104 | "FontSize": 14 105 | }, 106 | { 107 | "Text": "Boilers", 108 | "Position": {"x": 58, "y": 6.3, "z": 0}, 109 | "FontSize": 14 110 | }, 111 | { 112 | "Text": "Forklifts", 113 | "Position": {"x": 66, "y": -42, "z": 0}, 114 | "FontSize": 14 115 | } 116 | ], 117 | "StaticMarkers": [ 118 | { 119 | "Text": "Camera Bunker Door", 120 | "ImagePath": "Markers/exit.png", 121 | "Position": { "x": -14.5749989, "y": 40.36, "z": -2.086 }, 122 | "Category": "Extract", 123 | "ShowInRaid": false, 124 | "Color": { "r": 1.0, "g": 0.460784316, "b": 0.007843138, "a": 1.0 } 125 | }, 126 | { 127 | "Text": "Office Window", 128 | "ImagePath": "Markers/exit.png", 129 | "Position": { "x": 17.8332214, "y": 39.8851128, "z": 8.730333 }, 130 | "Category": "Extract", 131 | "ShowInRaid": false, 132 | "Color": { "r": 1.0, "g": 0.460784316, "b": 0.007843138, "a": 1.0 } 133 | }, 134 | { 135 | "Text": "Cellars", 136 | "ImagePath": "Markers/exit.png", 137 | "Position": { "x": 77.529, "y": -28.891, "z": -4.657 }, 138 | "Category": "Extract", 139 | "ShowInRaid": false, 140 | "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } 141 | }, 142 | { 143 | "Text": "Gate 3", 144 | "ImagePath": "Markers/exit.png", 145 | "Position": { "x": 58.709, "y": 64.112, "z": 0.84 }, 146 | "Category": "Extract", 147 | "ShowInRaid": false, 148 | "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } 149 | }, 150 | { 151 | "Text": "Gate 0", 152 | "ImagePath": "Markers/exit.png", 153 | "Position": { "x": -63.7400055, "y": 55.902, "z": 1.749 }, 154 | "Category": "Extract", 155 | "ShowInRaid": false, 156 | "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } 157 | }, 158 | { 159 | "Text": "Med Tent Gate", 160 | "ImagePath": "Markers/exit.png", 161 | "Position": { "x": -18.4857769, "y": -60.1718826, "z": 1.16133332 }, 162 | "Category": "Extract", 163 | "ShowInRaid": false, 164 | "Color": { "r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } 165 | }, 166 | { 167 | "Text": "Locked Office", 168 | "ImagePath": "Markers/door_with_lock.png", 169 | "Position": { "x": 29.2459984, "y": 36.52492, "z": 9.155067 }, 170 | "Category": "LockedDoor", 171 | "AssociatedItemId": "5448ba0b4bdc2d02308b456c", 172 | "Color": { "r": 1, "g": 0.921569, "b": 0.015686275, "a": 1.0 } 173 | }, 174 | { 175 | "Text": "Pumping Station", 176 | "ImagePath": "Markers/door_with_lock.png", 177 | "Position": { "x": 43.1875839, "y": -14.1608534, "z": 1.01413012 }, 178 | "Category": "LockedDoor", 179 | "AssociatedItemId": "57a349b2245977762b199ec7", 180 | "Color": { "r": 1, "g": 0.921569, "b": 0.015686275, "a": 1.0 } 181 | }, 182 | { 183 | "Text": "Gate 0", 184 | "ImagePath": "Markers/door_with_lock.png", 185 | "Position": { "x": -53.754, "y": 58.93033, "z": 2.33300042 }, 186 | "Category": "LockedDoor", 187 | "AssociatedItemId": "5448ba0b4bdc2d02308b456c", 188 | "Color": { "r": 1, "g": 0.921569, "b": 0.015686275, "a": 1.0 } 189 | }, 190 | { 191 | "Text": "Cellars", 192 | "ImagePath": "Markers/door_with_lock.png", 193 | "Position": { "x": 66.42518, "y": -29.8983536, "z": -1.53579187 }, 194 | "Category": "LockedDoor", 195 | "AssociatedItemId": "5448ba0b4bdc2d02308b456c", 196 | "Color": { "r": 1, "g": 0.921569, "b": 0.015686275, "a": 1.0 } 197 | }, 198 | { 199 | "Text": "Pumping Station", 200 | "ImagePath": "Markers/door_with_lock.png", 201 | "Position": { "x": 40.9448547, "y": -6.261415, "z": 0.9631303 }, 202 | "Category": "LockedDoor", 203 | "AssociatedItemId": "593858c486f774253a24cb52", 204 | "Color": { "r": 1, "g": 0.921569, "b": 0.015686275, "a": 1.0 } 205 | }, 206 | { 207 | "Text": "Med Tent", 208 | "ImagePath": "Markers/door_with_lock.png", 209 | "Position": { "x": -20.3113289, "y": -49.138, "z": 1.27600038 }, 210 | "Category": "LockedDoor", 211 | "AssociatedItemId": "5448ba0b4bdc2d02308b456c", 212 | "Color": { "r": 1, "g": 0.921569, "b": 0.015686275, "a": 1.0 } 213 | } 214 | ] 215 | } 216 | -------------------------------------------------------------------------------- /Resources/Maps/Factory_TarkovDev/Layers/SVG/Factory-Basement.svg: -------------------------------------------------------------------------------- 1 | 2 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /Resources/Maps/Factory_TarkovDev/Layers/SVG/Factory-Second_Floor.svg: -------------------------------------------------------------------------------- 1 | 2 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /Resources/Maps/Factory_TarkovDev/Layers/SVG/Factory-Third_Floor.svg: -------------------------------------------------------------------------------- 1 | 2 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /Resources/Maps/Factory_TarkovDev/Layers/factory_layer_-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Factory_TarkovDev/Layers/factory_layer_-1.png -------------------------------------------------------------------------------- /Resources/Maps/Factory_TarkovDev/Layers/factory_layer_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Factory_TarkovDev/Layers/factory_layer_0.png -------------------------------------------------------------------------------- /Resources/Maps/Factory_TarkovDev/Layers/factory_layer_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Factory_TarkovDev/Layers/factory_layer_1.png -------------------------------------------------------------------------------- /Resources/Maps/Factory_TarkovDev/Layers/factory_layer_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Factory_TarkovDev/Layers/factory_layer_2.png -------------------------------------------------------------------------------- /Resources/Maps/GroundZero_TarkovDev/GroundZero_TarkovDev.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "DisplayName": "Ground Zero (TarkovDev)", 3 | "Author": "Tarkov.dev", 4 | "AuthorLink": "https://tarkov.dev", 5 | "MapInternalNames": [ "Sandbox", "Sandbox_high" ], 6 | "CoordinateRotation": 180, 7 | "Bounds": { 8 | "Min": {"x": -99, "y": -124}, 9 | "Max": {"x": 249, "y": 364} 10 | }, 11 | "DefaultLevel": 0, 12 | "Layers": { 13 | "Garage": { 14 | "Level": -1, 15 | "ImagePath": "Maps/GroundZero_TarkovDev/Layers/ground_zero_layer_-1.png", 16 | "ImageBounds": { 17 | "Min": {"x": -99, "y": -124}, 18 | "Max": {"x": 249, "y": 364} 19 | }, 20 | "GameBounds": [ 21 | { // garage 22 | "Min": {"x": 43, "y": -100, "z": -100}, 23 | "Max": {"x": 117, "y": 190, "z": 21} 24 | }, 25 | { // underpass 26 | "Min": {"x": 117, "y": 49, "z": -100}, 27 | "Max": {"x": 143, "y": 80, "z": 21} 28 | } 29 | ] 30 | }, 31 | "Ground Floor": { 32 | "Level": 0, 33 | "ImagePath": "Maps/GroundZero_TarkovDev/Layers/ground_zero_layer_0.png", 34 | "ImageBounds": { 35 | "Min": {"x": -99, "y": -124}, 36 | "Max": {"x": 249, "y": 364} 37 | }, 38 | "GameBounds": [ 39 | { 40 | "Min": {"x": -99, "y": -124, "z": -100}, 41 | "Max": {"x": 249, "y": 364, "z": 100} 42 | } 43 | ] 44 | }, 45 | "2nd Floor": { 46 | "Level": 1, 47 | "ImagePath": "Maps/GroundZero_TarkovDev/Layers/ground_zero_layer_1.png", 48 | "ImageBounds": { 49 | "Min": {"x": -99, "y": -124}, 50 | "Max": {"x": 249, "y": 364} 51 | }, 52 | "GameBounds": [ 53 | { // rest of 2nd floor 54 | "Min": {"x": -99, "y": -124, "z": 28}, 55 | "Max": {"x": 249, "y": 364, "z": 32.3} 56 | }, 57 | { // m showroom 58 | "Min": {"x": 91, "y": 216, "z": 26}, 59 | "Max": {"x": 98, "y": 228, "z": 31} 60 | } 61 | ] 62 | }, 63 | "3rd Floor": { 64 | "Level": 2, 65 | "ImagePath": "Maps/GroundZero_TarkovDev/Layers/ground_zero_layer_2.png", 66 | "ImageBounds": { 67 | "Min": {"x": -99, "y": -124}, 68 | "Max": {"x": 249, "y": 364} 69 | }, 70 | "GameBounds": [ 71 | { 72 | "Min": {"x": -99, "y": -124, "z": 32.3}, 73 | "Max": {"x": 249, "y": 364, "z": 100} 74 | } 75 | ] 76 | } 77 | }, 78 | "Labels": [ 79 | { 80 | "Text": "TerraGroup", 81 | "Position": {"x": -50, "y": 0, "z": 0}, 82 | "FontSize": 16 83 | }, 84 | { 85 | "Text": "Skyside", 86 | "Position": {"x": 150, "y": 1, "z": 0}, 87 | "FontSize": 16 88 | }, 89 | { 90 | "Text": "Fusion", 91 | "Position": {"x": 141, "y": 142, "z": 0}, 92 | "FontSize": 16 93 | }, 94 | { 95 | "Text": "Empire", 96 | "Position": {"x": 14, "y": 201, "z": 0}, 97 | "FontSize": 16 98 | }, 99 | { 100 | "Text": "Capital Insight", 101 | "Position": {"x": 115, "y": 285, "z": 0}, 102 | "FontSize": 16 103 | }, 104 | { 105 | "Text": "Nakatani", 106 | "Position": {"x": 2, "y": 324, "z": 0}, 107 | "FontSize": 16 108 | }, 109 | { 110 | "Text": "Elemental Global", 111 | "Position": {"x": 80, "y": -118, "z": 0}, 112 | "FontSize": 16 113 | }, 114 | { 115 | "Text": "Oasis", 116 | "Position": {"x": 115, "y": 104, "z": 22}, 117 | "FontSize": 16 118 | }, 119 | { 120 | "Text": "ASAP Winery", 121 | "Position": {"x": 115, "y": 30, "z": 22}, 122 | "FontSize": 12 123 | }, 124 | { 125 | "Text": "Tarbank", 126 | "Position": {"x": 43, "y": 150, "z": 0}, 127 | "FontSize": 14 128 | }, 129 | { 130 | "Text": "GAGRIN Hotel", 131 | "Position": {"x": 58, "y": 234, "z": 0}, 132 | "FontSize": 12 133 | }, 134 | { 135 | "Text": "M Showroom", 136 | "Position": {"x": 97, "y": 223, "z": 0}, 137 | "FontSize": 12 138 | }, 139 | { 140 | "Text": "Science Offices", 141 | "Position": {"x": -13, "y": 48, "z": 29}, 142 | "FontSize": 12 143 | } 144 | ], 145 | "StaticMarkers": [ 146 | { 147 | "Text": "Scav Checkpoint (Co-op)", 148 | "ImagePath": "Markers/exit.png", 149 | "Position": { "x": 26.1550121, "y": -82.52659, "z": 24.5091858 }, 150 | "Category": "Extract", 151 | "ShowInRaid": false, 152 | "Color": { "r": 1.0, "g": 0.460784316, "b": 0.007843138, "a": 1.0 } 153 | }, 154 | { 155 | "Text": "Police Cordon V-Ex", 156 | "ImagePath": "Markers/exit.png", 157 | "Position": { "x": -19.5134068, "y": 114.942139, "z": 23.7110023 }, 158 | "Category": "Extract", 159 | "ShowInRaid": false, 160 | "Color": {"r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } 161 | }, 162 | { 163 | "Text": "Emercom Checkpoint", 164 | "ImagePath": "Markers/exit.png", 165 | "Position": { "x": 151.625, "y": -97.45658, "z": 24.662056 }, 166 | "Category": "Extract", 167 | "ShowInRaid": false, 168 | "Color": {"r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } 169 | }, 170 | { 171 | "Text": "Nakatani Basement Stairs", 172 | "ImagePath": "Markers/exit.png", 173 | "Position": { "x": -16.1249924, "y": 335.063416, "z": 14.7620564 }, 174 | "Category": "Extract", 175 | "ShowInRaid": false, 176 | "Color": {"r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } 177 | }, 178 | { 179 | "Text": "Mira Ave", 180 | "ImagePath": "Markers/exit.png", 181 | "Position": { "x": 218.225, "y": -38.5065842, "z": 17.9920559 }, 182 | "Category": "Extract", 183 | "ShowInRaid": false, 184 | "Color": {"r": 0.0, "g": 1.0, "b": 0.0, "a": 1.0 } 185 | }, 186 | { 187 | "Text": "Underground Parking Utility Room", 188 | "ImagePath": "Markers/door_with_lock.png", 189 | "Position": { "x": 107.056, "y": 50.6362534, "z": 13.499 }, 190 | "Category": "LockedDoor", 191 | "Color": { "r": 1.0, "g": 0.921568632, "b": 0.0156862754, "a": 1.0}, 192 | "AssociatedItemId": "658199972dc4e60f6d556a2f" 193 | }, 194 | { 195 | "Text": "TerraGroup Science Offices", 196 | "ImagePath": "Markers/door_with_lock.png", 197 | "Position": { "x": -17.975647, "y": 58.5532341, "z": 30.7062054 }, 198 | "Category": "LockedDoor", 199 | "Color": { "r": 1.0, "g": 0.921568632, "b": 0.0156862754, "a": 1.0}, 200 | "AssociatedItemId": "658199aa38c79576a2569e13" 201 | } 202 | ] 203 | } 204 | -------------------------------------------------------------------------------- /Resources/Maps/GroundZero_TarkovDev/Layers/SVG/GroundZero-Second_Floor.svg: -------------------------------------------------------------------------------- 1 | 2 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Resources/Maps/GroundZero_TarkovDev/Layers/SVG/GroundZero-Third_Floor.svg: -------------------------------------------------------------------------------- 1 | 2 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Resources/Maps/GroundZero_TarkovDev/Layers/SVG/GroundZero-Underground_Level.svg: -------------------------------------------------------------------------------- 1 | 2 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /Resources/Maps/GroundZero_TarkovDev/Layers/ground_zero_layer_-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/GroundZero_TarkovDev/Layers/ground_zero_layer_-1.png -------------------------------------------------------------------------------- /Resources/Maps/GroundZero_TarkovDev/Layers/ground_zero_layer_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/GroundZero_TarkovDev/Layers/ground_zero_layer_0.png -------------------------------------------------------------------------------- /Resources/Maps/GroundZero_TarkovDev/Layers/ground_zero_layer_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/GroundZero_TarkovDev/Layers/ground_zero_layer_1.png -------------------------------------------------------------------------------- /Resources/Maps/GroundZero_TarkovDev/Layers/ground_zero_layer_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/GroundZero_TarkovDev/Layers/ground_zero_layer_2.png -------------------------------------------------------------------------------- /Resources/Maps/Interchange_TarkovDev/Layers/interchange_layer_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Interchange_TarkovDev/Layers/interchange_layer_0.png -------------------------------------------------------------------------------- /Resources/Maps/Interchange_TarkovDev/Layers/interchange_layer_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Interchange_TarkovDev/Layers/interchange_layer_1.png -------------------------------------------------------------------------------- /Resources/Maps/Interchange_TarkovDev/Layers/interchange_layer_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Interchange_TarkovDev/Layers/interchange_layer_2.png -------------------------------------------------------------------------------- /Resources/Maps/Labs_TarkovDev/Layers/labs_layer_-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Labs_TarkovDev/Layers/labs_layer_-1.png -------------------------------------------------------------------------------- /Resources/Maps/Labs_TarkovDev/Layers/labs_layer_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Labs_TarkovDev/Layers/labs_layer_0.png -------------------------------------------------------------------------------- /Resources/Maps/Labs_TarkovDev/Layers/labs_layer_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Labs_TarkovDev/Layers/labs_layer_1.png -------------------------------------------------------------------------------- /Resources/Maps/Lighthouse_TarkovData/Layers/lighthouse_layer_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Lighthouse_TarkovData/Layers/lighthouse_layer_0.png -------------------------------------------------------------------------------- /Resources/Maps/Reserve_TarkovData/Layers/SVG/Reserve-Bunkers.svg: -------------------------------------------------------------------------------- 1 | 2 | 28 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Resources/Maps/Reserve_TarkovData/Layers/reserve_layer_-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Reserve_TarkovData/Layers/reserve_layer_-1.png -------------------------------------------------------------------------------- /Resources/Maps/Reserve_TarkovData/Layers/reserve_layer_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Reserve_TarkovData/Layers/reserve_layer_0.png -------------------------------------------------------------------------------- /Resources/Maps/Shoreline_TarkovData/Layers/SVG/Shoreline-Underground_Level.svg: -------------------------------------------------------------------------------- 1 | 2 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /Resources/Maps/Shoreline_TarkovData/Layers/shoreline_layer_-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Shoreline_TarkovData/Layers/shoreline_layer_-1.png -------------------------------------------------------------------------------- /Resources/Maps/Shoreline_TarkovData/Layers/shoreline_layer_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Shoreline_TarkovData/Layers/shoreline_layer_0.png -------------------------------------------------------------------------------- /Resources/Maps/Shoreline_TarkovData/Layers/shoreline_layer_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Shoreline_TarkovData/Layers/shoreline_layer_1.png -------------------------------------------------------------------------------- /Resources/Maps/Shoreline_TarkovData/Layers/shoreline_layer_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Shoreline_TarkovData/Layers/shoreline_layer_2.png -------------------------------------------------------------------------------- /Resources/Maps/Streets_TarkovData/Layers/streets_layer_-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Streets_TarkovData/Layers/streets_layer_-1.png -------------------------------------------------------------------------------- /Resources/Maps/Streets_TarkovData/Layers/streets_layer_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Streets_TarkovData/Layers/streets_layer_0.png -------------------------------------------------------------------------------- /Resources/Maps/Streets_TarkovData/Layers/streets_layer_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Streets_TarkovData/Layers/streets_layer_1.png -------------------------------------------------------------------------------- /Resources/Maps/Streets_TarkovData/Layers/streets_layer_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Streets_TarkovData/Layers/streets_layer_2.png -------------------------------------------------------------------------------- /Resources/Maps/Streets_TarkovData/Layers/streets_layer_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Streets_TarkovData/Layers/streets_layer_3.png -------------------------------------------------------------------------------- /Resources/Maps/Streets_TarkovData/Layers/streets_layer_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Streets_TarkovData/Layers/streets_layer_4.png -------------------------------------------------------------------------------- /Resources/Maps/Woods_TarkovData/Layers/woods_layer_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Maps/Woods_TarkovData/Layers/woods_layer_0.png -------------------------------------------------------------------------------- /Resources/Maps/map_and_data_credits.txt: -------------------------------------------------------------------------------- 1 | Default map data used is from: 2 | - TarkovTracker/tarkovData: https://github.com/TarkovTracker/tarkovdata/ 3 | - TarkovDev: https://github.com/the-hideout/tarkov-dev 4 | 5 | Interactive web maps using this data are here: https://tarkov.dev/maps/ 6 | 7 | All marker data was datamined by this program, but additional info about those markers 8 | gotten from the above sources. 9 | 10 | 11 | Tarkov.dev License 12 | --- 13 | 14 | MIT License 15 | 16 | Copyright (c) 2019 Oskar Risberg 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy 19 | of this software and associated documentation files (the "Software"), to deal 20 | in the Software without restriction, including without limitation the rights 21 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | copies of the Software, and to permit persons to whom the Software is 23 | furnished to do so, subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in all 26 | copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 34 | SOFTWARE. 35 | 36 | --- 37 | -------------------------------------------------------------------------------- /Resources/Markers/airdrop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Markers/airdrop.png -------------------------------------------------------------------------------- /Resources/Markers/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Markers/arrow.png -------------------------------------------------------------------------------- /Resources/Markers/backpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Markers/backpack.png -------------------------------------------------------------------------------- /Resources/Markers/btr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Markers/btr.png -------------------------------------------------------------------------------- /Resources/Markers/car_door.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Markers/car_door.png -------------------------------------------------------------------------------- /Resources/Markers/car_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Markers/car_key.png -------------------------------------------------------------------------------- /Resources/Markers/door_with_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Markers/door_with_key.png -------------------------------------------------------------------------------- /Resources/Markers/door_with_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Markers/door_with_lock.png -------------------------------------------------------------------------------- /Resources/Markers/dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Markers/dot.png -------------------------------------------------------------------------------- /Resources/Markers/exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Markers/exit.png -------------------------------------------------------------------------------- /Resources/Markers/lever.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Markers/lever.png -------------------------------------------------------------------------------- /Resources/Markers/marker_credits.txt: -------------------------------------------------------------------------------- 1 | Marker Credits 2 | 3 | --- 4 | 5 | CC BY 3.0 License: https://creativecommons.org/licenses/by/3.0/deed.en 6 | 7 | Exit door icon by Delapouite under CC BY 3.0 8 | https://game-icons.net/1x1/delapouite/exit-door.html 9 | 10 | Locked door icon by Delapouite under CC BY 3.0 11 | https://game-icons.net/1x1/delapouite/locked-door.html 12 | 13 | Lever icon by Lorc under CC BY 3.0 14 | https://game-icons.net/1x1/lorc/lever.html 15 | 16 | Car door icon by Delapouite under CC BY 3.0 17 | https://game-icons.net/1x1/delapouite/car-door.html 18 | 19 | Key lock icon by Delapouite under CC BY 3.0 20 | https://game-icons.net/1x1/delapouite/key-lock.html 21 | 22 | Door icon by Delapouite under CC BY 3.0 23 | https://game-icons.net/1x1/delapouite/door.html 24 | 25 | Position marker icon by Delapouite under CC BY 3.0 26 | https://game-icons.net/1x1/delapouite/position-marker.html 27 | 28 | Parachute icon by Lorc under CC BY 3.0 29 | https://game-icons.net/1x1/lorc/parachute.html 30 | 31 | Death skull icon by sbed under CC BY 3.0 32 | https://game-icons.net/1x1/sbed/death-skull.html 33 | 34 | Backpack icon by Delapouite under CC BY 3.0 35 | https://game-icons.net/1x1/delapouite/backpack.html 36 | 37 | Above images were changed with the below changes 38 | - Scaled down to 60x60 images 39 | - Black outline added 40 | - Some icons combined or resized 41 | 42 | --- 43 | 44 | CC BY-NC-SA 4.0: https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en 45 | 46 | BTR icon by Kuromi under CC BY-NC-SA 4.0 47 | https://github.com/schkuromi/icons 48 | 49 | --- 50 | -------------------------------------------------------------------------------- /Resources/Markers/quest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Markers/quest.png -------------------------------------------------------------------------------- /Resources/Markers/skull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Markers/skull.png -------------------------------------------------------------------------------- /Resources/Markers/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Resources/Markers/star.png -------------------------------------------------------------------------------- /Screenshots/airdrop_marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Screenshots/airdrop_marker.png -------------------------------------------------------------------------------- /Screenshots/btr_marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Screenshots/btr_marker.png -------------------------------------------------------------------------------- /Screenshots/corpse_markers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Screenshots/corpse_markers.png -------------------------------------------------------------------------------- /Screenshots/dynamic_locks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Screenshots/dynamic_locks.png -------------------------------------------------------------------------------- /Screenshots/in_raid_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Screenshots/in_raid_map.png -------------------------------------------------------------------------------- /Screenshots/out_of_raid_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Screenshots/out_of_raid_map.png -------------------------------------------------------------------------------- /Screenshots/quest_markers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpstark/SPT-DynamicMaps/b6d8bf85ecc231af4c61d9ed6649d9a5cf241fde/Screenshots/quest_markers.png -------------------------------------------------------------------------------- /UI/Components/MapLabel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using DynamicMaps.Data; 3 | using DynamicMaps.Utils; 4 | using TMPro; 5 | using UnityEngine; 6 | 7 | namespace DynamicMaps.UI.Components 8 | { 9 | public class MapLabel : MonoBehaviour, ILayerBound 10 | { 11 | public string Text { get; protected set; } 12 | public string Category { get; protected set; } 13 | 14 | public TextMeshProUGUI Label { get; protected set; } 15 | public RectTransform RectTransform => gameObject.transform as RectTransform; 16 | 17 | private Vector3 _position; 18 | public Vector3 Position 19 | { 20 | get 21 | { 22 | return _position; 23 | } 24 | 25 | set 26 | { 27 | gameObject.GetRectTransform().anchoredPosition = value; 28 | _position = value; 29 | } 30 | } 31 | 32 | private Color _color = Color.white; 33 | public Color Color 34 | { 35 | get 36 | { 37 | return _color; 38 | } 39 | set 40 | { 41 | _color = value; 42 | Label.color = value; 43 | } 44 | } 45 | 46 | public Dictionary LabelAlphaLayerStatus { get; set; } = new Dictionary 47 | { 48 | {LayerStatus.Hidden, 0.0f}, 49 | {LayerStatus.Underneath, 0.0f}, 50 | {LayerStatus.OnTop, 1f}, 51 | {LayerStatus.FullReveal, 1f}, 52 | }; 53 | 54 | private bool _hasSetOutline = false; 55 | 56 | public static MapLabel Create(GameObject parent, MapLabelDef def, float degreesRotation, float scale) 57 | { 58 | var go = UIUtils.CreateUIGameObject(parent, $"MapLabel {def.Text}"); 59 | 60 | var rectTransform = go.GetRectTransform(); 61 | rectTransform.anchoredPosition = def.Position; 62 | rectTransform.localScale = scale * Vector2.one; 63 | rectTransform.localRotation = Quaternion.Euler(0, 0, degreesRotation - def.DegreesRotation); 64 | 65 | var label = go.AddComponent(); 66 | label.Text = def.Text; 67 | label.Category = def.Category; 68 | label.Position = def.Position; 69 | 70 | label.Label = go.AddComponent(); 71 | label.Color = def.Color; 72 | label.Label.autoSizeTextContainer = true; 73 | label.Label.fontSize = def.FontSize; 74 | label.Label.alignment = TextAlignmentOptions.Center; 75 | label.Label.text = label.Text; 76 | 77 | label._hasSetOutline = UIUtils.TrySetTMPOutline(label.Label); 78 | 79 | return label; 80 | } 81 | 82 | private void OnEnable() 83 | { 84 | if (_hasSetOutline || Label == null) 85 | { 86 | return; 87 | } 88 | 89 | _hasSetOutline = UIUtils.TrySetTMPOutline(Label); 90 | Label.text = Label.text; // try resetting text, since it seems like if outline fails, it doesn't size properly 91 | } 92 | 93 | public void HandleNewLayerStatus(LayerStatus status) 94 | { 95 | Label.color = new Color(Label.color.r, Label.color.g, Label.color.b, LabelAlphaLayerStatus[status]); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /UI/Components/MapLayer.cs: -------------------------------------------------------------------------------- 1 | using DynamicMaps.Data; 2 | using DynamicMaps.Utils; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | 6 | namespace DynamicMaps.UI.Components 7 | { 8 | public enum LayerStatus 9 | { 10 | Hidden, 11 | Underneath, 12 | OnTop, 13 | FullReveal 14 | } 15 | 16 | public interface ILayerBound 17 | { 18 | Vector3 Position { get; set; } 19 | void HandleNewLayerStatus(LayerStatus status); 20 | } 21 | 22 | public class MapLayer : MonoBehaviour 23 | { 24 | private static float _fadeMultiplierPerLayer = 0.5f; 25 | private static float _defaultLevelFallbackAlpha = 0.1f; 26 | 27 | public string Name { get; private set; } 28 | public Image Image { get; private set; } 29 | public RectTransform RectTransform => gameObject.transform as RectTransform; 30 | 31 | public int Level => _def.Level; 32 | public LayerStatus Status { get; private set; } 33 | public bool IsOnDefaultLevel { get; set; } 34 | 35 | private MapLayerDef _def = new MapLayerDef(); 36 | 37 | public static MapLayer Create(GameObject parent, string name, MapLayerDef def, float degreesRotation) 38 | { 39 | var go = new GameObject(name, typeof(RectTransform), typeof(CanvasRenderer)); 40 | go.layer = parent.layer; 41 | go.transform.SetParent(parent.transform); 42 | go.ResetRectTransform(); 43 | 44 | var rectTransform = go.GetRectTransform(); 45 | var layer = go.AddComponent(); 46 | 47 | // set layer size 48 | var size = def.ImageBounds.Max - def.ImageBounds.Min; 49 | var rotatedSize = MathUtils.GetRotatedRectangle(size, degreesRotation); 50 | rectTransform.sizeDelta = rotatedSize; 51 | 52 | // set layer offset 53 | var offset = MathUtils.GetMidpoint(def.ImageBounds.Min, def.ImageBounds.Max); 54 | rectTransform.anchoredPosition = offset; 55 | 56 | // set rotation to combat when we rotate the whole map content 57 | rectTransform.localRotation = Quaternion.Euler(0, 0, degreesRotation); 58 | 59 | layer.Name = name; 60 | layer._def = def; 61 | 62 | // load image 63 | layer.Image = go.AddComponent(); 64 | layer.Image.raycastTarget = false; 65 | layer.Image.sprite = TextureUtils.GetOrLoadCachedSprite(def.ImagePath); 66 | layer.Image.type = Image.Type.Simple; 67 | 68 | return layer; 69 | } 70 | 71 | public bool IsCoordinateInLayer(Vector3 coordinate) 72 | { 73 | foreach (var gameBound in _def.GameBounds) 74 | { 75 | if (coordinate.x > gameBound.Min.x && coordinate.x < gameBound.Max.x 76 | && coordinate.y > gameBound.Min.y && coordinate.y < gameBound.Max.y 77 | && coordinate.z > gameBound.Min.z && coordinate.z < gameBound.Max.z) 78 | { 79 | return true; 80 | } 81 | } 82 | 83 | return false; 84 | } 85 | 86 | public float GetMatchingBoundVolume(Vector3 coordinate) 87 | { 88 | // a bit scuffed formatting 89 | // this assumes that a layer wouldn't have multiple overlapping game bounds 90 | foreach (var gameBound in _def.GameBounds) 91 | { 92 | if (coordinate.x > gameBound.Min.x && coordinate.x < gameBound.Max.x 93 | && coordinate.y > gameBound.Min.y && coordinate.y < gameBound.Max.y 94 | && coordinate.z > gameBound.Min.z && coordinate.z < gameBound.Max.z) 95 | { 96 | return (gameBound.Max.x - gameBound.Min.x) * 97 | (gameBound.Max.y - gameBound.Min.y) * 98 | (gameBound.Max.z - gameBound.Min.z); 99 | } 100 | } 101 | 102 | return float.MinValue; 103 | } 104 | 105 | public void OnTopLevelSelected(int newLevel) 106 | { 107 | Status = LayerStatus.Hidden; 108 | if (Level == newLevel) 109 | { 110 | Status = LayerStatus.OnTop; 111 | } 112 | else if (Level < newLevel) 113 | { 114 | Status = LayerStatus.Underneath; 115 | } 116 | 117 | var isActive = true; 118 | var levelDelta = newLevel - Level; 119 | var c = Mathf.Clamp01(Mathf.Pow(_fadeMultiplierPerLayer, levelDelta)); 120 | var a = 1f; 121 | 122 | if (Status == LayerStatus.Hidden) 123 | { 124 | isActive = false; 125 | 126 | if (IsOnDefaultLevel) 127 | { 128 | a = _defaultLevelFallbackAlpha; 129 | isActive = true; 130 | } 131 | } 132 | 133 | gameObject.SetActive(isActive); 134 | Image.color = new Color(c, c, c, a); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /UI/Components/MapPeekComponent.cs: -------------------------------------------------------------------------------- 1 | using BepInEx.Configuration; 2 | using DynamicMaps.Utils; 3 | using UnityEngine; 4 | 5 | namespace DynamicMaps.UI.Components 6 | { 7 | internal class MapPeekComponent : MonoBehaviour 8 | { 9 | public ModdedMapScreen MapScreen { get; set; } 10 | public RectTransform MapScreenTrueParent { get; set; } 11 | 12 | public RectTransform RectTransform { get; private set; } 13 | public KeyboardShortcut PeekShortcut { get; set; } 14 | public bool HoldForPeek { get; set; } // opposite is peek toggle 15 | public bool IsPeeking { get; private set; } 16 | 17 | internal static MapPeekComponent Create(GameObject parent) 18 | { 19 | var go = UIUtils.CreateUIGameObject(parent, "MapPeek"); 20 | go.GetRectTransform().sizeDelta = parent.GetRectTransform().sizeDelta; 21 | 22 | var component = go.AddComponent(); 23 | 24 | return component; 25 | } 26 | 27 | private void Awake() 28 | { 29 | RectTransform = gameObject.GetRectTransform(); 30 | } 31 | 32 | private void Update() 33 | { 34 | if (HoldForPeek && PeekShortcut.BetterIsPressed() != IsPeeking) 35 | { 36 | // hold for peek 37 | if (PeekShortcut.BetterIsPressed()) 38 | { 39 | BeginPeek(); 40 | } 41 | else 42 | { 43 | EndPeek(); 44 | } 45 | } 46 | else if (!HoldForPeek && PeekShortcut.BetterIsDown()) 47 | { 48 | // toggle peek 49 | if (!IsPeeking) 50 | { 51 | BeginPeek(); 52 | } 53 | else 54 | { 55 | EndPeek(); 56 | } 57 | } 58 | } 59 | 60 | internal void BeginPeek() 61 | { 62 | if (IsPeeking) 63 | { 64 | return; 65 | } 66 | 67 | // just in case something else is attached and tries to be in front 68 | transform.SetAsLastSibling(); 69 | 70 | IsPeeking = true; 71 | 72 | // attach map screen to peek mask 73 | MapScreen.transform.SetParent(RectTransform); 74 | MapScreen.Show(); 75 | } 76 | 77 | internal void EndPeek() 78 | { 79 | if (!IsPeeking) 80 | { 81 | return; 82 | } 83 | 84 | IsPeeking = false; 85 | 86 | // un-attach map screen and re-attach to true parent 87 | MapScreen.Hide(); 88 | MapScreen.transform.SetParent(MapScreenTrueParent); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /UI/Components/PlayerMapMarker.cs: -------------------------------------------------------------------------------- 1 | using DynamicMaps.Utils; 2 | using EFT; 3 | using UnityEngine; 4 | 5 | namespace DynamicMaps.UI.Components 6 | { 7 | public class PlayerMapMarker : MapMarker 8 | { 9 | private static float _maxCallbackTime = 0.5f; // how often to call callback in seconds 10 | private static Vector2 _pivot = new Vector2(0.5f, 0.5f); 11 | 12 | public IPlayer Player { get; private set; } 13 | 14 | private float _callbackTime = _maxCallbackTime; // make sure to start with a callback 15 | 16 | public static PlayerMapMarker Create(IPlayer player, GameObject parent, string imagePath, Color color, string category, 17 | Vector2 size, float degreesRotation, float scale) 18 | { 19 | var name = $"{player.Profile.GetCorrectedNickname()}"; 20 | var marker = Create(parent, name, category, imagePath, color, 21 | MathUtils.ConvertToMapPosition(player.Position), 22 | size, _pivot, degreesRotation, scale); 23 | marker.IsDynamic = true; 24 | marker.Player = player; 25 | 26 | return marker; 27 | } 28 | 29 | public PlayerMapMarker() 30 | { 31 | ImageAlphaLayerStatus[LayerStatus.Hidden] = 0.25f; 32 | ImageAlphaLayerStatus[LayerStatus.Underneath] = 0.25f; 33 | ImageAlphaLayerStatus[LayerStatus.OnTop] = 1f; 34 | ImageAlphaLayerStatus[LayerStatus.FullReveal] = 1f; 35 | 36 | LabelAlphaLayerStatus[LayerStatus.Hidden] = 0.0f; 37 | LabelAlphaLayerStatus[LayerStatus.Underneath] = 0.0f; 38 | LabelAlphaLayerStatus[LayerStatus.OnTop] = 0.0f; 39 | LabelAlphaLayerStatus[LayerStatus.FullReveal] = 1.00f; 40 | } 41 | 42 | private void LateUpdate() 43 | { 44 | if (Player?.Transform?.Original == null) 45 | { 46 | return; 47 | } 48 | 49 | // throttle callback, since that leads to a layer search which might be expensive 50 | _callbackTime += Time.deltaTime; 51 | var callback = _callbackTime >= _maxCallbackTime; 52 | if (callback) 53 | { 54 | _callbackTime = 0f; 55 | } 56 | 57 | MoveAndRotate(MathUtils.ConvertToMapPosition(Player.Position), -Player.Rotation.x, callback); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /UI/Components/TransformMapMarker.cs: -------------------------------------------------------------------------------- 1 | using Comfort.Common; 2 | using DynamicMaps.Utils; 3 | using UnityEngine; 4 | 5 | namespace DynamicMaps.UI.Components 6 | { 7 | public class TransformMapMarker : MapMarker 8 | { 9 | private static float _maxCallbackTime = 0.5f; // how often to call callback in seconds 10 | private static Vector2 _pivot = new Vector2(0.5f, 0.5f); 11 | 12 | public Transform FollowingTransform { get; private set; } 13 | public MathUtils.RotationAxis RotationAxis { get; set; } = MathUtils.RotationAxis.Y; 14 | 15 | private float _callbackTime = _maxCallbackTime; // make sure to start with a callback 16 | private bool _warnedAttachedIsDisabled = false; 17 | 18 | public static TransformMapMarker Create(Transform followingTransform, GameObject parent, string imagePath, Color color, 19 | string name, string category, Vector2 size, float degreesRotation, float scale) 20 | { 21 | var marker = Create(parent, name, category, imagePath, color, 22 | MathUtils.ConvertToMapPosition(followingTransform), 23 | size, _pivot, degreesRotation, scale); 24 | marker.IsDynamic = true; 25 | marker.FollowingTransform = followingTransform; 26 | 27 | return marker; 28 | } 29 | 30 | public TransformMapMarker() 31 | { 32 | ImageAlphaLayerStatus[LayerStatus.Hidden] = 0.25f; 33 | ImageAlphaLayerStatus[LayerStatus.Underneath] = 0.25f; 34 | ImageAlphaLayerStatus[LayerStatus.OnTop] = 1f; 35 | ImageAlphaLayerStatus[LayerStatus.FullReveal] = 1f; 36 | 37 | LabelAlphaLayerStatus[LayerStatus.Hidden] = 0.0f; 38 | LabelAlphaLayerStatus[LayerStatus.Underneath] = 0.0f; 39 | LabelAlphaLayerStatus[LayerStatus.OnTop] = 0.0f; 40 | LabelAlphaLayerStatus[LayerStatus.FullReveal] = 1.00f; 41 | } 42 | 43 | private void LateUpdate() 44 | { 45 | if (FollowingTransform == null) 46 | { 47 | return; 48 | } 49 | 50 | if (!FollowingTransform.gameObject.activeInHierarchy) 51 | { 52 | if (!_warnedAttachedIsDisabled) 53 | { 54 | Plugin.Log.LogWarning($"FollowingTransform for TransformMapMarker has been disabled without removing the map marker"); 55 | Color = Color.red; 56 | _warnedAttachedIsDisabled = true; 57 | } 58 | return; 59 | } 60 | else 61 | { 62 | _warnedAttachedIsDisabled = false; 63 | } 64 | 65 | var mapPosition = MathUtils.ConvertToMapPosition(FollowingTransform); 66 | var mapRotation = MathUtils.ConvertToMapRotation(FollowingTransform, RotationAxis); 67 | 68 | // check if in exactly the same place and skip updating if it is 69 | if (MathUtils.ApproxEquals(Position.x, mapPosition.x) 70 | && MathUtils.ApproxEquals(Position.y, mapPosition.y) 71 | && MathUtils.ApproxEquals(Position.z, mapPosition.z) 72 | && MathUtils.ApproxEquals(Rotation, mapRotation)) 73 | { 74 | return; 75 | } 76 | 77 | // throttle callback, since that leads to a layer search which might be expensive 78 | _callbackTime += Time.deltaTime; 79 | var callback = _callbackTime >= _maxCallbackTime; 80 | if (callback) 81 | { 82 | _callbackTime = 0f; 83 | } 84 | 85 | MoveAndRotate(mapPosition, mapRotation, callback); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /UI/Controls/AbstractTextControl.cs: -------------------------------------------------------------------------------- 1 | using DynamicMaps.Utils; 2 | using TMPro; 3 | using UnityEngine; 4 | 5 | namespace DynamicMaps.UI.Controls 6 | { 7 | public abstract class AbstractTextControl : MonoBehaviour 8 | { 9 | public TextMeshProUGUI Text { get; protected set; } 10 | public RectTransform RectTransform => gameObject.transform as RectTransform; 11 | 12 | private bool _hasSetOutline = false; 13 | 14 | public static T Create(GameObject parent, string name, float fontSize) where T : AbstractTextControl 15 | { 16 | var go = UIUtils.CreateUIGameObject(parent, name); 17 | 18 | var textControl = go.AddComponent(); 19 | textControl.Text = go.AddComponent(); 20 | textControl.Text.autoSizeTextContainer = true; 21 | textControl.Text.fontSize = fontSize; 22 | textControl.Text.alignment = TextAlignmentOptions.Left; 23 | 24 | textControl._hasSetOutline = UIUtils.TrySetTMPOutline(textControl.Text); 25 | 26 | return textControl; 27 | } 28 | 29 | private void OnEnable() 30 | { 31 | if (_hasSetOutline || Text == null) 32 | { 33 | return; 34 | } 35 | 36 | _hasSetOutline = UIUtils.TrySetTMPOutline(Text); 37 | Text.text = Text.text; // try resetting text, since it seems like if outline fails, it doesn't size properly 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /UI/Controls/CursorPositionText.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace DynamicMaps.UI.Controls 4 | { 5 | public class CursorPositionText : AbstractTextControl 6 | { 7 | private RectTransform _mapViewTransform; 8 | 9 | public static CursorPositionText Create(GameObject parent, RectTransform mapViewTransform, float fontSize) 10 | { 11 | var text = Create(parent, "CursorPositionText", fontSize); 12 | text._mapViewTransform = mapViewTransform; 13 | 14 | return text; 15 | } 16 | 17 | private void Update() 18 | { 19 | RectTransformUtility.ScreenPointToLocalPointInRectangle( 20 | _mapViewTransform, Input.mousePosition, null, out Vector2 mouseRelative); 21 | Text.text = $"Cursor: {mouseRelative.x:F} {mouseRelative.y:F}"; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /UI/Controls/LevelSelectSlider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using DynamicMaps.Data; 5 | using DynamicMaps.Utils; 6 | using EFT.UI; 7 | using TMPro; 8 | using UnityEngine; 9 | using UnityEngine.UI; 10 | 11 | namespace DynamicMaps.UI.Controls 12 | { 13 | public class LevelSelectSlider : MonoBehaviour 14 | { 15 | private static float _levelTextSize = 15f; 16 | private static Vector2 _levelTextOffset = new Vector2(10f, 0f); 17 | 18 | public event Action OnLevelSelectedBySlider; 19 | public RectTransform RectTransform => gameObject.transform as RectTransform; 20 | 21 | private int _selectedLevel = int.MinValue; 22 | public int SelectedLevel 23 | { 24 | get 25 | { 26 | return _selectedLevel; 27 | } 28 | 29 | set 30 | { 31 | if (_selectedLevel == value && !_levels.Contains(value)) 32 | { 33 | return; 34 | } 35 | 36 | if (_levels.Count == 1) 37 | { 38 | _scrollbar.value = 0.5f; 39 | } 40 | else 41 | { 42 | _scrollbar.value = _levels.IndexOf(value) / (_levels.Count - 1f); 43 | } 44 | 45 | _text.text = $"Level {value}"; 46 | _selectedLevel = value; 47 | } 48 | } 49 | 50 | private TextMeshProUGUI _text; 51 | private Scrollbar _scrollbar; 52 | private List _levels = new List(); 53 | private bool _hasSetOutline = false; 54 | 55 | public static LevelSelectSlider Create(GameObject prefab, Transform parent) 56 | { 57 | var go = GameObject.Instantiate(prefab); 58 | go.name = "LevelSelectScrollbar"; 59 | go.transform.SetParent(parent); 60 | go.transform.localScale = Vector3.one; 61 | 62 | // position to top left 63 | var oldPosition = go.GetRectTransform().anchoredPosition; 64 | 65 | // remove useless component 66 | GameObject.Destroy(go.GetComponent()); 67 | 68 | var slider = go.AddComponent(); 69 | return slider; 70 | } 71 | 72 | private void Awake() 73 | { 74 | // create layer text 75 | var slidingArea = gameObject.transform.Find("Scrollbar/Sliding Area/Handle").gameObject; 76 | var layerTextGO = UIUtils.CreateUIGameObject(slidingArea, "SlidingLayerText"); 77 | _text = layerTextGO.AddComponent(); 78 | _text.fontSize = _levelTextSize; 79 | _text.alignment = TextAlignmentOptions.Left; 80 | _text.GetRectTransform().offsetMin = Vector2.zero; 81 | _text.GetRectTransform().offsetMax = Vector2.zero; 82 | _text.GetRectTransform().anchoredPosition = _levelTextOffset; 83 | 84 | _hasSetOutline = UIUtils.TrySetTMPOutline(_text); 85 | 86 | // setup the scrollbar component 87 | var actualScrollbarGO = gameObject.transform.Find("Scrollbar").gameObject; 88 | _scrollbar = actualScrollbarGO.GetComponent(); 89 | _scrollbar.direction = Scrollbar.Direction.BottomToTop; 90 | _scrollbar.onValueChanged.AddListener(OnScrollbarChanged); 91 | _scrollbar.onValueChanged.SetPersistentListenerState(0, UnityEngine.Events.UnityEventCallState.Off); 92 | 93 | // setup the +/- buttons 94 | var plusButton = gameObject.transform.Find("Plus").gameObject.GetComponent