using SanAndreasUnity.Behaviours.Vehicles; using SanAndreasUnity.Importing.Conversion; using SanAndreasUnity.Utilities; using System; using System.Collections; using System.IO; using UnityEngine; using UnityEngine.UI; using Object = UnityEngine.Object; namespace SanAndreasUnity.Behaviours { public class MiniMap : MonoBehaviour { public const int tileEdge = 12; // width/height of map in tiles public const int tileCount = tileEdge * tileEdge; // number of tiles public const int mapEdge = 6000; // width/height of map in world coordinates public const int texSize = 128; // width/height of single tile in px public const int mapSize = tileEdge * texSize; // width/height of whole map in px public const int uiSize = 256, uiOffset = 10; public static MiniMap Instance { get; private set; } public Image northImage, outlineImage, maskImage, mapImage; public RawImage playerImage; public RectTransform mapTransform, maskTransform, mapContainer, northPivot; public Text zoneNameLabel; private Canvas _canvas; private const float maxVelocityForZooming = 300f; public static readonly float[] availableZooms = new float[] { .5f, .75f, 1f, 1.2f, 1.4f, 1.6f, 2f, 2.5f, 3f, 5f }; public float zoom = 1.3f; public float zoomDuration = 1; private float curZoomPercentage; public int zoomSelector = 2; private Coroutine zoomCoroutine; public Vector3 FocusPos { get; set; } = Vector3.zero; public bool IsMinimapVisible => _canvas.enabled && this.gameObject.activeInHierarchy; private float _timeWhenRetrievedZoneName = 0f; private string _lastZoneName = ""; private string ZoneName { get { if (Time.time - _timeWhenRetrievedZoneName > 2f) { _timeWhenRetrievedZoneName = Time.time; _lastZoneName = Zone.GetZoneName(this.FocusPos); } return _lastZoneName; } } public Texture2D NorthBlip { get; private set; } public Texture2D PlayerBlip { get; private set; } public Texture2D WaypointTexture { get; private set; } public Texture2D VehicleTexture { get; private set; } public Texture2D GreenHouseTexture { get; private set; } public Texture2D MapTexture { get; private set; } public Texture2D BlackPixel { get; private set; } public Texture2D SeaPixel { get; private set; } private Ped m_ped => Ped.Instance; private PlayerController m_playerController => PlayerController.Instance; private TextureDictionary huds; private Sprite mapSprite; public void Load() { LoadGameTextures(); BlackPixel = new Texture2D(1, 1); BlackPixel.SetPixel(0, 0, new Color(0, 0, 0, .5f)); BlackPixel.Apply(); SeaPixel = new Texture2D(1, 1); SeaPixel.SetPixel(0, 0, new Color(.45f, .54f, .678f)); SeaPixel.Apply(); } private void LoadGameTextures() { MapTexture = new Texture2D(mapSize, mapSize, TextureFormat.ARGB32, false, true); TextureLoadParams textureLoadParams = new TextureLoadParams() { makeNoLongerReadable = false }; for (int i = 0; i < tileCount; i++) { // Offset int y = ((i / tileEdge) + 1) * texSize, x = (i % tileEdge) * texSize; string name = "radar" + ((i < 10) ? "0" : "") + i; var texDict = TextureDictionary.Load(name); Texture2D tex = texDict.GetDiffuse(name, textureLoadParams).Texture; for (int ii = 0; ii < texSize; ++ii) for (int jj = 0; jj < texSize; ++jj) MapTexture.SetPixel(x + ii, texSize - (y + jj) - 1, tex.GetPixel(ii, jj)); // unload the texture (don't destroy it, because it can be a dummy texture) } MapTexture.Apply(false, true); huds = TextureDictionary.Load("hud"); NorthBlip = huds.GetDiffuse("radar_north").Texture; PlayerBlip = huds.GetDiffuse("radar_centre").Texture; WaypointTexture = huds.GetDiffuse("radar_waypoint").Texture; VehicleTexture = huds.GetDiffuse("radar_impound").Texture; GreenHouseTexture = huds.GetDiffuse("radar_propertyG").Texture; northImage.sprite = Sprite.Create(NorthBlip, new Rect(0, 0, NorthBlip.width, NorthBlip.height), new Vector2(NorthBlip.width, NorthBlip.height) / 2); playerImage.texture = this.PlayerBlip; mapSprite = Sprite.Create(MapTexture, new Rect(0, 0, MapTexture.width, MapTexture.height), new Vector2(MapTexture.width, MapTexture.height) / 2); mapImage.sprite = mapSprite; } private void Awake() { Instance = this; _canvas = this.GetComponentInParent(); curZoomPercentage = availableZooms[zoomSelector]; } private void ReadInput() { bool zoomIn = Input.GetKeyDown(KeyCode.N); bool zoomOut = Input.GetKeyDown(KeyCode.B); if (zoomIn) ++zoomSelector; else if (zoomOut) --zoomSelector; if (zoomIn || zoomOut) { if (zoomCoroutine != null) StopCoroutine(zoomCoroutine); zoomCoroutine = StartCoroutine(ChangeZoom(zoomIn)); } } public static Vector2 WorldPosToMapPos(Vector3 worldPos) { // map center is at (0,0) world coordinates // this, for example, means that the left edge of the world is at: -mapEdge / 2.0f // adjust world position, so that (0,0) world coordinates are mapped to (0,0) map coordinates worldPos += new Vector3(mapEdge / 2.0f, 0, mapEdge / 2.0f); float mul = mapSize / (float)mapEdge; return new Vector2(worldPos.x * mul, worldPos.z * mul); } public static Vector3 MapPosToWorldPos(Vector2 mapPos) { // adjust map position, so that (0,0) map coordinated are mapped to (0,0) world coordinates mapPos -= Vector2.one * (mapSize * 0.5f); float mul = mapEdge / (float)mapSize; return new Vector3(mapPos.x * mul, 0.0f, mapPos.y * mul); } private void LateUpdate() { if (!this.IsMinimapVisible) return; // update focus position var ped = m_ped; if (ped != null) this.FocusPos = ped.transform.position; else if (Camera.main != null) this.FocusPos = Camera.main.transform.position; // update zoom based on ped's velocity var playerController = m_playerController; if (playerController != null) zoom = Mathf.Lerp(.9f, 1.3f, 1 - Mathf.Clamp(playerController.CurVelocity, 0, maxVelocityForZooming) / maxVelocityForZooming) * curZoomPercentage; // read input if (GameManager.CanPlayerReadInput()) { this.ReadInput(); } // update position of UI int worldSize = mapSize * 4; Vector3 mapPos = - new Vector3(this.FocusPos.x, this.FocusPos.z, 0f) * mapSize / (float)worldSize; mapImage.rectTransform.localPosition = mapPos; // update rotation of UI float relAngle = Camera.main != null ? Camera.main.transform.eulerAngles.y : 0f; //mapContainer.pivot = new Vector2(mapPos.x, mapPos.y); mapContainer.localRotation = Quaternion.Euler(0, 0, relAngle); mapContainer.localScale = new Vector3(zoom, zoom, 1); if (northPivot != null) northPivot.localRotation = Quaternion.Euler(0, 0, relAngle); if (playerImage != null && m_ped != null) playerImage.rectTransform.localRotation = Quaternion.Euler(0, 0, relAngle - (m_ped.transform.eulerAngles.y + 180)); // update zone name label string currentZoneName = this.ZoneName; if (currentZoneName != this.zoneNameLabel.text) this.zoneNameLabel.text = currentZoneName; } private IEnumerator ChangeZoom(bool isIncreasing) { float fAlpha = 1; zoomSelector = GetClampedZoomSelector(zoomSelector); float curZoom = availableZooms[zoomSelector % availableZooms.Length], lastZoom = availableZooms[GetClampedZoomSelector(zoomSelector - 1 * (isIncreasing ? 1 : -1)) % availableZooms.Length]; float t = 0; while (t < zoomDuration) { curZoomPercentage = Mathf.Lerp(lastZoom, curZoom, t / zoomDuration); yield return new WaitForFixedUpdate(); t += Time.fixedDeltaTime; fAlpha -= Time.fixedDeltaTime / zoomDuration; } zoomCoroutine = null; } private int GetClampedZoomSelector(int? val = null) { int zoomVal = val == null ? zoomSelector : val.Value; if (zoomVal < 0) zoomVal = availableZooms.Length - 1; return zoomVal; } } }