< Summary

Class:DCL.MapRenderer
Assembly:MapRenderer
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/MapRenderer/MapRenderer.cs
Covered lines:85
Uncovered lines:87
Coverable lines:172
Total lines:394
Line coverage:49.4% (85 of 172)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
MapRenderer()0%110100%
Awake()0%110100%
Initialize()0%220100%
EnsurePools()0%3.043083.33%
OnDestroy()0%110100%
Cleanup()0%550100%
Update()0%3.882022.22%
UpdateCursorMapCoords()0%6200%
IsCursorOverMapChunk()0%6200%
UpdateParcelHighlight()0%90900%
UpdateParcelHold()0%12300%
OnKernelConfigChanged(...)0%2100%
CoordinatesAreInsideTheWorld(...)0%12300%
MapRenderer_OnSceneInfoUpdated(...)0%10.0410092.86%
IsEmptyParcel(...)0%220100%
OnOtherPlayersAdded(...)0%2100%
OnOtherPlayerRemoved(...)0%6200%
ConfigureUserIcon(...)0%2100%
OnCharacterMove(...)0%2.152066.67%
OnCharacterRotate(...)0%2100%
OnCharacterSetPosition(...)0%6200%
UpdateRendering(...)0%2100%
UpdateBackgroundLayer(...)0%2100%
UpdateSelectionLayer()0%2100%
UpdateOverlayLayer()0%2100%
GetViewportCenter()0%2100%
ClickMousePositionParcel()0%6200%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/MapRenderer/MapRenderer.cs

#LineLine coverage
 1using DCL.Helpers;
 2using System.Collections.Generic;
 3using UnityEngine;
 4using UnityEngine.UI;
 5using UnityEngine.EventSystems;
 6using TMPro;
 7using KernelConfigurationTypes;
 8
 9namespace DCL
 10{
 11    public class MapRenderer : MonoBehaviour
 12    {
 13        const int LEFT_BORDER_PARCELS = 25;
 14        const int RIGHT_BORDER_PARCELS = 31;
 15        const int TOP_BORDER_PARCELS = 31;
 16        const int BOTTOM_BORDER_PARCELS = 25;
 17        const int WORLDMAP_WIDTH_IN_PARCELS = 300;
 18        const string MINIMAP_USER_ICONS_POOL_NAME = "MinimapUserIconsPool";
 19        const int MINIMAP_USER_ICONS_MAX_PREWARM = 30;
 20        private const int MAX_CURSOR_PARCEL_DISTANCE = 40;
 21        private const int MAX_SCENE_CHARACTER_TITLE = 29;
 22        private const string EMPTY_PARCEL_NAME = "Empty parcel";
 23        private int NAVMAP_CHUNK_LAYER;
 24
 64125        public static MapRenderer i { get; private set; }
 26
 1227        [SerializeField] private float parcelHightlightScale = 1.25f;
 28        [SerializeField] private Button ParcelHighlightButton;
 29
 30        [HideInInspector] public Vector3 cursorMapCoords;
 1231        [HideInInspector] public bool showCursorCoords = true;
 32        [HideInInspector] public event System.Action OnMovedParcelCursor;
 033        public Vector3 playerGridPosition => Utils.WorldToGridPositionUnclamped(playerWorldPosition.Get());
 34        public MapAtlas atlas;
 35        public RawImage parcelHighlightImage;
 36        public TextMeshProUGUI highlightedParcelText;
 37        public Transform overlayContainer;
 38        public Transform globalUserMarkerContainer;
 39        public RectTransform playerPositionIcon;
 40
 41        public static System.Action<int, int> OnParcelClicked;
 42        public static System.Action OnCursorFarFromParcel;
 43
 1244        public float scaleFactor = 1f;
 45
 46        // Used as a reference of the coordinates origin in-map and as a parcel width/height reference
 47        public RectTransform centeredReferenceParcel;
 48
 49        public MapSceneIcon scenesOfInterestIconPrefab;
 50        public GameObject userIconPrefab;
 51        public UserMarkerObject globalUserMarkerPrefab;
 52
 053        public MapGlobalUsersPositionMarkerController usersPositionMarkerController { private set; get; }
 54
 55        private float parcelSizeInMap;
 2256        private Vector3Variable playerWorldPosition => CommonScriptableObjects.playerWorldPosition;
 2257        private Vector3Variable playerRotation => CommonScriptableObjects.cameraForward;
 1258        private Vector3[] mapWorldspaceCorners = new Vector3[4];
 59        private Vector3 worldCoordsOriginInMap;
 1260        private List<RaycastResult> uiRaycastResults = new List<RaycastResult>();
 1261        private PointerEventData uiRaycastPointerEventData = new PointerEventData(EventSystem.current);
 62
 1263        private HashSet<MinimapMetadata.MinimapSceneInfo> scenesOfInterest = new HashSet<MinimapMetadata.MinimapSceneInf
 1264        private Dictionary<MinimapMetadata.MinimapSceneInfo, GameObject> scenesOfInterestMarkers = new Dictionary<Minima
 1265        private Dictionary<string, PoolableObject> usersInfoMarkers = new Dictionary<string, PoolableObject>();
 66
 67        private Vector3 lastClickedCursorMapCoords;
 68        private Pool usersInfoPool;
 69
 4470        private BaseDictionary<string, Player> otherPlayers => DataStore.i.player.otherPlayers;
 71        private bool isInitialized = false;
 72
 73        private bool parcelHighlightEnabledValue = false;
 74
 1275        List<WorldRange> validWorldRanges = new List<WorldRange>
 76        {
 77            new WorldRange(-150, -150, 150, 150) // default range
 78        };
 79
 80        public bool parcelHighlightEnabled
 81        {
 82            set
 83            {
 084                parcelHighlightEnabledValue = value;
 085                parcelHighlightImage.gameObject.SetActive(parcelHighlightEnabledValue);
 086            }
 087            get { return parcelHighlightEnabledValue; }
 88        }
 89
 90        private void Awake()
 91        {
 1092            i = this;
 1093            Initialize();
 1094        }
 95
 96        public void Initialize()
 97        {
 1198            if (isInitialized)
 199                return;
 100
 10101            isInitialized = true;
 10102            EnsurePools();
 10103            atlas.InitializeChunks();
 10104            NAVMAP_CHUNK_LAYER = LayerMask.NameToLayer("NavmapChunk");
 105
 10106            MinimapMetadata.GetMetadata().OnSceneInfoUpdated += MapRenderer_OnSceneInfoUpdated;
 10107            otherPlayers.OnAdded += OnOtherPlayersAdded;
 10108            otherPlayers.OnRemoved += OnOtherPlayerRemoved;
 109
 10110            ParcelHighlightButton.onClick.AddListener(ClickMousePositionParcel);
 111
 10112            playerWorldPosition.OnChange += OnCharacterMove;
 10113            playerRotation.OnChange += OnCharacterRotate;
 114
 10115            parcelHighlightImage.rectTransform.localScale = new Vector3(parcelHightlightScale, parcelHightlightScale, 1f
 116
 10117            usersPositionMarkerController = new MapGlobalUsersPositionMarkerController(globalUserMarkerPrefab,
 118                globalUserMarkerContainer,
 119                MapUtils.GetTileToLocalPosition);
 120
 10121            usersPositionMarkerController.SetUpdateMode(MapGlobalUsersPositionMarkerController.UpdateMode.BACKGROUND);
 122
 10123            KernelConfig.i.OnChange += OnKernelConfigChanged;
 10124        }
 125
 126        private void EnsurePools()
 127        {
 10128            usersInfoPool = PoolManager.i.GetPool(MINIMAP_USER_ICONS_POOL_NAME);
 129
 10130            if (usersInfoPool == null)
 131            {
 10132                usersInfoPool = PoolManager.i.AddPool(
 133                    MINIMAP_USER_ICONS_POOL_NAME,
 134                    Instantiate(userIconPrefab.gameObject, overlayContainer.transform),
 135                    maxPrewarmCount: MINIMAP_USER_ICONS_MAX_PREWARM,
 136                    isPersistent: true);
 137
 10138                if (!Configuration.EnvironmentSettings.RUNNING_TESTS)
 0139                    usersInfoPool.ForcePrewarm();
 140            }
 10141        }
 142
 20143        public void OnDestroy() { Cleanup(); }
 144
 145        public void Cleanup()
 146        {
 12147            if (atlas != null)
 11148                atlas.Cleanup();
 149
 26150            foreach (var kvp in scenesOfInterestMarkers)
 151            {
 1152                if (kvp.Value != null)
 1153                    Destroy(kvp.Value);
 154            }
 155
 12156            scenesOfInterestMarkers.Clear();
 157
 12158            playerWorldPosition.OnChange -= OnCharacterMove;
 12159            playerRotation.OnChange -= OnCharacterRotate;
 12160            MinimapMetadata.GetMetadata().OnSceneInfoUpdated -= MapRenderer_OnSceneInfoUpdated;
 12161            otherPlayers.OnAdded -= OnOtherPlayersAdded;
 12162            otherPlayers.OnRemoved -= OnOtherPlayerRemoved;
 163
 12164            ParcelHighlightButton.onClick.RemoveListener(ClickMousePositionParcel);
 165
 12166            usersPositionMarkerController?.Dispose();
 167
 12168            KernelConfig.i.OnChange -= OnKernelConfigChanged;
 169
 12170            isInitialized = false;
 12171        }
 172
 173        void Update()
 174        {
 4175            if (!parcelHighlightEnabled)
 4176                return;
 177
 0178            parcelSizeInMap = centeredReferenceParcel.rect.width * centeredReferenceParcel.lossyScale.x;
 179
 180            // the reference parcel has a bottom-left pivot
 0181            centeredReferenceParcel.GetWorldCorners(mapWorldspaceCorners);
 0182            worldCoordsOriginInMap = mapWorldspaceCorners[0];
 183
 0184            UpdateCursorMapCoords();
 185
 0186            UpdateParcelHighlight();
 187
 0188            UpdateParcelHold();
 0189        }
 190
 191        void UpdateCursorMapCoords()
 192        {
 0193            if (!IsCursorOverMapChunk())
 0194                return;
 195
 0196            cursorMapCoords = Input.mousePosition - worldCoordsOriginInMap;
 0197            cursorMapCoords = cursorMapCoords / parcelSizeInMap;
 198
 0199            cursorMapCoords.x = (int)Mathf.Floor(cursorMapCoords.x);
 0200            cursorMapCoords.y = (int)Mathf.Floor(cursorMapCoords.y);
 0201        }
 202
 203        bool IsCursorOverMapChunk()
 204        {
 0205            uiRaycastPointerEventData.position = Input.mousePosition;
 0206            EventSystem.current.RaycastAll(uiRaycastPointerEventData, uiRaycastResults);
 207
 0208            return uiRaycastResults.Count > 0 && uiRaycastResults[0].gameObject.layer == NAVMAP_CHUNK_LAYER;
 209        }
 210
 211        void UpdateParcelHighlight()
 212        {
 0213            if (!CoordinatesAreInsideTheWorld((int)cursorMapCoords.x, (int)cursorMapCoords.y))
 214            {
 0215                if (parcelHighlightImage.gameObject.activeSelf)
 0216                    parcelHighlightImage.gameObject.SetActive(false);
 217
 0218                return;
 219            }
 220
 0221            if (!parcelHighlightImage.gameObject.activeSelf)
 0222                parcelHighlightImage.gameObject.SetActive(true);
 223
 0224            string previousText = highlightedParcelText.text;
 0225            parcelHighlightImage.rectTransform.SetAsLastSibling();
 0226            parcelHighlightImage.rectTransform.anchoredPosition = MapUtils.GetTileToLocalPosition(cursorMapCoords.x, cur
 0227            highlightedParcelText.text = showCursorCoords ? $"{cursorMapCoords.x}, {cursorMapCoords.y}" : string.Empty;
 228
 0229            if (highlightedParcelText.text != previousText && !Input.GetMouseButton(0))
 230            {
 0231                OnMovedParcelCursor?.Invoke();
 232            }
 233
 234            // ----------------------------------------------------
 235            // TODO: Use sceneInfo to highlight whole scene parcels and populate scenes hover info on navmap once we can
 236            // var sceneInfo = mapMetadata.GetSceneInfo(cursorMapCoords.x, cursorMapCoords.y);
 0237        }
 238
 239        void UpdateParcelHold()
 240        {
 0241            if(Vector3.Distance(lastClickedCursorMapCoords, cursorMapCoords) > MAX_CURSOR_PARCEL_DISTANCE / (scaleFactor
 242            {
 0243                OnCursorFarFromParcel?.Invoke();
 244            }
 0245        }
 246
 0247        private void OnKernelConfigChanged(KernelConfigModel current, KernelConfigModel previous) { validWorldRanges = c
 248
 249        bool CoordinatesAreInsideTheWorld(int xCoord, int yCoord)
 250        {
 0251            foreach (WorldRange worldRange in validWorldRanges)
 252            {
 0253                if (worldRange.Contains(xCoord, yCoord))
 254                {
 0255                    return true;
 256                }
 257            }
 0258            return false;
 0259        }
 260
 261        private void MapRenderer_OnSceneInfoUpdated(MinimapMetadata.MinimapSceneInfo sceneInfo)
 262        {
 3263            if (!sceneInfo.isPOI)
 2264                return;
 265
 1266            if (scenesOfInterest.Contains(sceneInfo))
 0267                return;
 268
 1269            if (IsEmptyParcel(sceneInfo))
 0270                return;
 271
 1272            scenesOfInterest.Add(sceneInfo);
 273
 1274            GameObject go = Object.Instantiate(scenesOfInterestIconPrefab.gameObject, overlayContainer.transform);
 275
 1276            Vector2 centerTile = Vector2.zero;
 277
 10278            foreach (var parcel in sceneInfo.parcels)
 279            {
 4280                centerTile += parcel;
 281            }
 282
 1283            centerTile /= (float)sceneInfo.parcels.Count;
 1284            float distance = float.PositiveInfinity;
 1285            Vector2 centerParcel = Vector2.zero;
 10286            foreach (var parcel in sceneInfo.parcels)
 287            {
 4288                if (Vector2.Distance(centerTile, parcel) < distance)
 289                {
 1290                    distance = Vector2.Distance(centerParcel, parcel);
 1291                    centerParcel = parcel;
 292                }
 293
 294            }
 295
 1296            (go.transform as RectTransform).anchoredPosition = MapUtils.GetTileCenterToLocalPosition(centerParcel.x, cen
 297
 1298            MapSceneIcon icon = go.GetComponent<MapSceneIcon>();
 299
 1300            if (icon.title != null)
 1301                icon.title.text = sceneInfo.name.Length > MAX_SCENE_CHARACTER_TITLE ? sceneInfo.name.Substring(0, MAX_SC
 302
 1303            scenesOfInterestMarkers.Add(sceneInfo, go);
 1304        }
 305
 306        private bool IsEmptyParcel(MinimapMetadata.MinimapSceneInfo sceneInfo)
 307        {
 1308            return (sceneInfo.name != null && sceneInfo.name.Equals(EMPTY_PARCEL_NAME));
 309        }
 310
 311        private void OnOtherPlayersAdded(string userId, Player player)
 312        {
 0313            var poolable = usersInfoPool.Get();
 0314            var marker = poolable.gameObject.GetComponent<MapUserIcon>();
 0315            marker.gameObject.name = $"UserIcon-{player.name}";
 0316            marker.gameObject.transform.SetParent(overlayContainer.transform, true);
 0317            marker.Populate(player);
 0318            usersInfoMarkers.Add(userId, poolable);
 0319        }
 320
 321        private void OnOtherPlayerRemoved(string userId, Player player)
 322        {
 0323            if (!usersInfoMarkers.TryGetValue(userId, out PoolableObject go))
 324            {
 0325                return;
 326            }
 327
 0328            usersInfoPool.Release(go);
 0329            usersInfoMarkers.Remove(userId);
 0330        }
 331
 332        private void ConfigureUserIcon(GameObject iconGO, Vector3 pos)
 333        {
 0334            var gridPosition = Utils.WorldToGridPositionUnclamped(pos);
 0335            iconGO.transform.localPosition = MapUtils.GetTileToLocalPosition(gridPosition.x, gridPosition.y);
 0336        }
 337
 338        private void OnCharacterMove(Vector3 current, Vector3 previous)
 339        {
 10340            current.y = 0;
 10341            previous.y = 0;
 342
 10343            if (Vector3.Distance(current, previous) < 0.1f)
 10344                return;
 345
 0346            UpdateRendering(Utils.WorldToGridPositionUnclamped(current));
 0347        }
 348
 0349        private void OnCharacterRotate(Vector3 current, Vector3 previous) { UpdateRendering(Utils.WorldToGridPositionUnc
 350
 351        public void OnCharacterSetPosition(Vector2Int newCoords, Vector2Int oldCoords)
 352        {
 0353            if (oldCoords == newCoords)
 0354                return;
 355
 0356            UpdateRendering(new Vector2((float)newCoords.x, (float)newCoords.y));
 0357        }
 358
 359        public void UpdateRendering(Vector2 newCoords)
 360        {
 0361            UpdateBackgroundLayer(newCoords);
 0362            UpdateSelectionLayer();
 0363            UpdateOverlayLayer();
 0364        }
 365
 0366        void UpdateBackgroundLayer(Vector2 newCoords) { atlas.CenterToTile(newCoords); }
 367
 368        void UpdateSelectionLayer()
 369        {
 370            //TODO(Brian): Build and place here the scene highlight if applicable.
 0371        }
 372
 373        void UpdateOverlayLayer()
 374        {
 375            //NOTE(Brian): Player icon
 0376            Vector3 f = CommonScriptableObjects.cameraForward.Get();
 0377            Quaternion playerAngle = Quaternion.Euler(0, 0, Mathf.Atan2(-f.x, f.z) * Mathf.Rad2Deg);
 378
 0379            var gridPosition = playerGridPosition;
 0380            playerPositionIcon.anchoredPosition = MapUtils.GetTileToLocalPosition(gridPosition.x, gridPosition.y);
 0381            playerPositionIcon.rotation = playerAngle;
 0382        }
 383
 0384        public Vector3 GetViewportCenter() { return atlas.viewport.TransformPoint(atlas.viewport.rect.center); }
 385
 386        // Called by the parcelhighlight image button
 387        public void ClickMousePositionParcel()
 388        {
 0389            highlightedParcelText.text = string.Empty;
 0390            lastClickedCursorMapCoords = new Vector3((int)cursorMapCoords.x, (int)cursorMapCoords.y, 0);
 0391            OnParcelClicked?.Invoke((int)cursorMapCoords.x, (int)cursorMapCoords.y);
 0392        }
 393    }
 394}