Astroon Idle

Astroon Idle, an idle manager mobile game, offers players a dynamic experience with:

  • Unlocking and upgrading buildings for specific purposes
  • Merging hexagon mines to gather basic resources
  • Refining resources to create ship parts
  • Strategically upgrading individual Astroons for optimal performance
  • Managing the crucial solar energy resource through merging solar panels
Astroon Idle intricately weaves resource management, merging mechanics, and strategic upgrades, delivering an engaging and rewarding idle gaming experience.

Code Snippets

Game Resources

[Serializable]
    public class GameResources
    {
        [SerializeField]
        internal Dictionary<ResourceType, Resource> Resources = new Dictionary<ResourceType, Resource>();

        public GameResources()
        {
            Resources = new Dictionary<ResourceType, Resource>();
        }

        public GameResources(Dictionary<ResourceType, Resource> resources)
        {
            Resources = resources;
        }

        public bool HasAtLeast(GameResources requirements)
        {
            if (requirements == null)
                return true;

            foreach (var requirement in requirements.Resources)
            {
                if (!(Resources.ContainsKey(requirement.Key) && requirement.Value.Quantity > AlphabeticNotation.zero) ||
                    Resources[requirement.Key] < requirement.Value)
                    return false;
            }


            return true;
        }

        public void AddOrCreateResource(ResourceType resourceType, Resource resourceToAdd)
        {
            if (Resources.ContainsKey(resourceType))
            {
                Resources[resourceType] += resourceToAdd;
            }
            else
            {
                Resources.Add(resourceType, resourceToAdd);
            }
        }

        public static GameResources operator +(GameResources a, GameResources b)
        {
            var result = new GameResources(); //a;
            if (a != null)
            {
                foreach (var kvp in a.Resources)
                {
                    result.AddOrCreateResource(kvp.Key, kvp.Value);
                }
            }

            if (b != null)
            {
                foreach (var kvp in b.Resources)
                {
                    result.AddOrCreateResource(kvp.Key, kvp.Value);
                }
            }

            return result;
        }

        public static GameResources operator -(GameResources a, GameResources b)
        {
            var result = new GameResources(); //a;
            if (a != null)
            {
                foreach (var kvp in a.Resources)
                {
                    result.AddOrCreateResource(kvp.Key, kvp.Value);
                }
            }

            if (b != null)
            {
                foreach (var kvp in b.Resources)
                {
                    if (result.Resources.TryGetValue(kvp.Key, out _))
                    {
                        result.Resources[kvp.Key] -= kvp.Value;
                    }
                    else
                    {
                        Debug.LogError(kvp.Key + " not found!");
                    }
                }
            }

            return result;
        }
    }

Mine Merger

public class MineMergerRemake : MergerRemake
{
    [SerializeField] private MineDataSORemake mineDataSo;
    [SerializeField] private ResourceType mineralType;
    [SerializeField] private SpriteRenderer bgSpriteRenderer;
    [SerializeField] private SpriteRenderer mineSpriteRenderer;
    [SerializeField] private MineRemake ownMine;
    [SerializeField] private HexagonRemake ownHex;
    private RaycastHit2D[] _hit = new RaycastHit2D[2];

    private void Start()
    {
        OnMinePressedDown += TurnGreenIfMergeable;
        OnMinePressedUp += TurnToPreviousColorFromGreen;
    }

    private void OnDisable()
    {
        OnMinePressedDown -= TurnGreenIfMergeable;
        OnMinePressedUp -= TurnToPreviousColorFromGreen;
    }

    protected override void OnMouseDown()
    {
        Debug.Log($"This mine's initial position: {InitialPos}, Level: {MergeLevel}");

        if (MergeLevel == 0)
            return;


        base.OnMouseDown();
        OnMinePressedDown?.Invoke(this, new MineEventArgs
        {
            Level = MergeLevel,
            MineralType = mineralType
        });
        SetSelectedOrderInLayer();
    }

    protected override void OnMouseUp()
    {
        PanZoom.Instance.mapIsMovable = true;

        if (MergeLevel == 0)
            return;

        int hitCount = Physics2D.RaycastNonAlloc(transform.position, Vector2.zero, _hit);

        if (hitCount is 0 or 1)
        {
            transform.position = InitialPos;
            OnMinePressedUp?.Invoke(this, EventArgs.Empty);
            return;
        }

        RaycastHit2D targetHit = default;

        foreach (var hit in _hit)
        {
            Debug.Log($"Hit: {hit.transform.gameObject.name}");
            if (!hit.transform.TryGetComponent(out MineMergerRemake selfMerger))
                continue;
            if (selfMerger == this)
                continue;
            targetHit = hit;
            break;
        }

        if (targetHit.collider != null)
        {
            Debug.Log($"Raycast hit {targetHit.transform.name}");
            if (targetHit.transform.TryGetComponent(out MineMergerRemake otherMineMerger))
            {
                if (otherMineMerger != this)
                {
                    if (otherMineMerger.MergeLevel == 0)
                    {
                        if (otherMineMerger.ownMine.IsDeactivated())
                        {
                            SwitchPlaces(otherMineMerger.transform);
                        }

                        OnMinePressedUp?.Invoke(this, EventArgs.Empty);
                        return;
                    }

                    if (otherMineMerger.mineralType == mineralType)
                    {
                        if (otherMineMerger.MergeLevel == MergeLevel)
                        {
                            Merge(otherMineMerger);
                            OnMinePressedUp?.Invoke(this, EventArgs.Empty);
                            Debug.Log($"Merged {targetHit.transform.name}!");
                            return;
                        }

                        Debug.LogError("Merge levels are not the same");
                    }
                    else
                    {
                        Debug.LogError("Mineral types are different");
                    }
                }
                else
                {
                    Debug.LogError($"Trying to merge with itself");
                }
            }
            else
            {
                Debug.LogError($"{targetHit.transform.name} does not have a merger");
            }
        }
        else
        {
            Debug.LogError("Ray did not hit a mergeable thing!");
        }

        transform.position = InitialPos;
        OnMinePressedUp?.Invoke(this, EventArgs.Empty);
    }

    public new event EventHandler<MineEventArgs> OnMerge;
    public event EventHandler<MineEventArgs> OnBought;

    public static event EventHandler<MineEventArgs> OnMergeStatic;
    public static event EventHandler OnBoughtStatic;

    public static event EventHandler<MineEventArgs> OnMinePressedDown;
    public static event EventHandler OnMinePressedUp;
    public static event EventHandler OnPlaceSwitched;

    private void TurnGreenIfMergeable(object sender, MineEventArgs e)
    {
        if (e.Level == MergeLevel && e.MineralType == mineralType)
            bgSpriteRenderer.color = Color.green;
    }

    private void TurnToPreviousColorFromGreen(object sender, EventArgs e)
    {
        if (ownHex.BoostLocation != BoostLocationRemake.None)
        {
            bgSpriteRenderer.color = Color.yellow;
            return;
        }

        if (bgSpriteRenderer.color != Color.green)
            return;
        bgSpriteRenderer.color = Color.white;
    }

    private void AdjustToBeingBought()
    {
        SetNormalOrderInLayer();
        bgSpriteRenderer.sprite = mineDataSo.GetBackgroundSprite(mineralType);
        mineSpriteRenderer.sprite = mineDataSo.GetMineSprite(MergeLevel, mineralType);
    }

    protected override void Merge(MergerRemake otherMerger)
    {
        base.Merge(otherMerger);

        MineMergerRemake otherMineMerger = otherMerger as MineMergerRemake;

        otherMineMerger.SetVisuals();
        RemoveVisuals();

        MineManager.Instance.AddNewMine(otherMineMerger.ownMine);
        MineManager.Instance.RemoveMine(ownMine);
        MineManager.Instance.CalculateMineCountsAndGeneration();

        OnMerge?.Invoke(this, new MineEventArgs { Level = MergeLevel, MineralType = mineralType });
        otherMineMerger.OnMerge?.Invoke(otherMineMerger,
            new MineEventArgs
            {
                Level = otherMineMerger.MergeLevel, MineralType = otherMineMerger.mineralType,
                mineSprite = otherMineMerger.bgSpriteRenderer.sprite
            });
        OnMergeStatic?.Invoke(this,
            new MineEventArgs
            {
                Level = otherMineMerger.MergeLevel, MineralType = otherMineMerger.mineralType,
                mineSprite = otherMineMerger.mineSpriteRenderer.sprite
            });
    }

    private void SwitchPlaces(Transform otherHex)
    {
        if (!otherHex.TryGetComponent(out HexagonRemake hex) || !hex.GetVisibleStatus())
        {
            transform.position = InitialPos;
            return;
        }


        transform.position = otherHex.position;
        otherHex.position = InitialPos;

        OnPlaceSwitched?.Invoke(this, EventArgs.Empty);
    }

    private void SetVisuals()
    {
        SetNormalOrderInLayer();
        bgSpriteRenderer.sprite = mineDataSo.GetBackgroundSprite(mineralType);
        mineSpriteRenderer.sprite = mineDataSo.GetMineSprite(MergeLevel, mineralType);
    }

    private void RemoveVisuals()
    {
        bgSpriteRenderer.sortingOrder = MineSortingLayersSO.EMPTY_ORDER_IN_LAYER;
        bgSpriteRenderer.sprite = mineDataSo.emptyBg;
        mineSpriteRenderer.sprite = null;
    }

    public ResourceType GetMineralType()
    {
        return mineralType;
    }

    public void SetMineralType(ResourceType targetMineralType)
    {
        mineralType = targetMineralType;
    }

    public void GetBought()
    {
        MergeLevel += 1;
        AdjustToBeingBought();
        MineManager.Instance.AddNewMine(ownMine);
        MineManager.Instance.CalculateMineCountsAndGeneration();
        OnBought?.Invoke(this, new MineEventArgs { Level = MergeLevel, MineralType = mineralType });
    }

    private void SetNormalOrderInLayer()
    {
        bgSpriteRenderer.sortingOrder = MineSortingLayersSO.MINE_BG_ORDER_IN_LAYER;
        mineSpriteRenderer.sortingOrder = MineSortingLayersSO.MINE_UNIT_ORDER_IN_LAYER;
    }

    private void SetSelectedOrderInLayer()
    {
        bgSpriteRenderer.sortingOrder = MineSortingLayersSO.SELECTED_MINE_BG_ORDER_IN_LAYER;
        mineSpriteRenderer.sortingOrder = MineSortingLayersSO.SELECTED_MINE_UNIT_ORDER_IN_LAYER;
    }
}