StackOverflowException: The requested operation caused a stack overflow - c#

I've made some code in Unity2d with c# and interface.
LevelingSystem.cs is putted in empty gameobject.
And i getting error:
StackOverflowException: The requested operation caused a stack overflow.
PlayerCap.get_actExp () (at Assets/Scripts/player/PlayerCap.cs:17)
PlayerCap.get<message truncated>
CapLevel.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
interface ICapLevel
{
float multiplierA { get; set; }
float multiplierB { get; set; }
float multiplierC { get; set; }
float multiplierD { get; set; }
int lvlCap { get; set; }
List<double> expCap { get; set; }
float actExp { get; set; }
void GenerateExpPerLvl();
float RequiredExp(int level);
}
PlayerCap.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCap : ICapLevel
{
public float multiplierA { get { return multiplierA; } set { multiplierA = 280f; } }
public float multiplierB { get { return multiplierB; } set { multiplierB = 100f; } }
public float multiplierC { get { return multiplierC; } set { multiplierA = 1.15f; } }
public float multiplierD { get { return multiplierD; } set { multiplierD = 2.3f; } }
public int lvlCap { get { return lvlCap; } set { lvlCap = 210; } }
public List<double> expCap { get { return expCap; } set { expCap = new List<double>(); } }
public float actExp { get { return actExp; } set { actExp = 0f; } }
public void GenerateExpPerLvl()
{
Debug.Log("implementation successful");
for (int expLevel = 1; expLevel <= lvlCap; expLevel++)
{
expCap.Add(RequiredExp(expLevel - 1));
}
}
public float RequiredExp(int level)
{
double formulaRounded;
var formula = multiplierB * (Mathf.Pow(multiplierC, level - 1)) + multiplierA * (Mathf.Pow(level, multiplierD));
if (formula < 1000)
{
if ((formula % 10) == 0)
{
formulaRounded = formula;
return (float)formulaRounded;
}
else
{
formulaRounded = Math.Round((formula / 1000), 1, MidpointRounding.AwayFromZero);
}
}
else
{
formulaRounded = Math.Round((formula / 1000), 0, MidpointRounding.AwayFromZero);
}
return (float)formulaRounded * 1000;
}
}
LevelingSystem.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LevelingSystem : MonoBehaviour
{
//interface implementation
private PlayerCap _pCap;
private ICapLevel _ICap;
private List<double> _pExpCap;
private int pLvl;
private float _pActExp;
public void Awake()
{
_pCap = new PlayerCap();
_ICap = _pCap;
}
private void Start()
{
_pActExp = _ICap.actExp;
_ICap.GenerateExpPerLvl();
_pExpCap = _ICap.expCap;
}
public void AddExp(float amount)
{
_pActExp += amount;
StartCoroutine(CapLevelCheck(amount));
}
private IEnumerator CapLevelCheck(float amount)
{
while (true)
{
if (_pActExp >= _pExpCap[pLvl])
{
_pActExp -= (float)_pExpCap[pLvl];
AddLevel(1);
} else
{
Debug.Log("You Get " + amount + " Experience and you have " + pLvl + " Lvl. *Click noice!");
break; //otherwise loop will do in infinite
}
yield return 0;
}
}
public void AddLevel(int amount)
{
pLvl += amount;
}
public int GetActualLevel()
{
return pLvl;
}
}
Im try few things like get rid off for in method GenerateExpPerLvl() and error gones away so my suspicious thing is list variable but maybe im wrong.
Thanks for help :)

The problem is here:
public float actExp { get { return actExp; } set { actExp = 0f; } }
// ^ Problem here & ^ same problem here
You are calling your Property inside its own getter and setter. It creates an infinite loop... (everytime you call actExp it will call itself)
Instead you should just write (if there is no logic inside accessors):
public float actExp { get; set; }

Related

Pass class object by reference to method override

I've been attempting to write a section of code for this project so that it can print out whichever BasePlayer object's hand along with the total as such (: (card) (card) (total) - (playerName)) and I've been trying to override ToString() to be able to accomplish this. Issue is I need to pass the referenced object's specific hand but can't figure out how to pass by reference while using an override. I'm not sure if it's syntax or how I'm doing it, I would just like to fix it.
The issue is the bottom most function:
using System;
using System.Collections.Generic;
using System.Text;
using Blackjack_C;
namespace Blackjack_C
{
public class BasePlayer : Hand
{
public string name;
private (ref BasePlayer ap);
public BasePlayer(ref string name)
{
this.name = name;
}
Hand hand = new Hand();
public bool IsBusted()
{
return hand.getTotal() > 21;
}
public void Bust()
{
Console.Write(name + " busts.");
}
public override string ToString(ref BasePlayer ap)
{
string output = ":\t";
if (!(ap.m_Cards.Count == 0))
{
foreach (var c in ap.m_Cards)
{
output += (c + "\t");
}
if (ap.getTotal() != 0)
{
output += ("(" + ap.getTotal() + ")");
}
return output;
}
else
{
return("<empty>");
}
}
}
}
Here is the definition of the Hand class:
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using Blackjack_C;
namespace Blackjack_C
{
public class Hand
{
protected List<Card> m_Cards;
~Hand()
{
clearHand();
}
public void add(Card pCard)
{
m_Cards.Insert(m_Cards.Count, pCard);
}
public void clearHand()
{
m_Cards.Clear();
}
public int getTotal()
{
// If the hand is empty it cannot get total
if (m_Cards.Count == 0)
{
return 0;
}
// Gets total value of deck (doesn't run if Dealer's hand)
int total = 0;
foreach (var c in m_Cards)
{
total += c.getValue();
}
// Bool to check if the hand has an Ace in it (doesn't run if Dealer's hand)
bool containsAce = false;
foreach (var c in m_Cards)
{
if (c.getValue() == (int) Card.card_face.Ace)
{
containsAce = true;
}
}
// Checks if hand is dealer's hand and get's value whilst keeping it secret from the player
if (m_Cards.First().getValue() == 0)
{
//int total = 0;
foreach (var c in m_Cards)
{
total += c.getDealerValue();
}
//bool containsAce = false;
foreach (var c in m_Cards)
{
if (c.getDealerValue() == (int) Card.card_face.Ace)
{
containsAce = true;
}
}
}
// Checks if total is less than 11 to see if changing Ace to = 11 instead of = 1 is necessary
if (containsAce && total <= 11)
{
total += 10;
}
return total;
}
public Card getCard(int index)
{
if (index >= m_Cards.Count || index < 0)
return m_Cards[0];
else
{
return m_Cards[index];
}
}
}
}
Sorry for lack of a good post, this is my first time posting a question here.
i created the hand object with assumptions. Hand can have 2 or more card.
so you can try like this :
public class BasePlayer
{
public Hand Hand { get; set; }
public string Name { get; set; }
public BasePlayer(ref string name)
{
Name = name;
}
public bool IsBusted()
{
return Hand.GetTotal() > 21;
}
public void Bust()
{
Console.Write(Name + " busts.");
}
public override string ToString()
{
// you can access referenced string and specific hand on here
return string.Join(' ',Hand.Cards.Select(card=>$"{(card)}")) + $" {(Hand.GetTotal()) } "+ Name;
}
}
public class Hand
{
public List<int> Cards { get; set; }
public int GetTotal()
{
return Cards.Sum();
}
}

Unity saving via playerprefs bugged?

Sorry, I that I included all the code but I really can't find the error: I play and save and after I buy something with the BuyTaxes or Buymarket and quit and stop it and play it again it shows all the UI shows and all the prices for the market and the discount use are set to 0. But I cant figure out why.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameScript : MonoBehaviour
{
public Text GoldText;
public Text ArmyPowerText;
public Text PlayerNametext;
public Text Button1Text;
public Text Button2Text;
public int gold;
public int armypower;
public string PlayerName;
public int Level;
public int Income;
public int incomeboost;
public int discount;
public float lvlremainder;
public GameObject ButtonHolder;
public Slider LevelBar;
public float LevelProgress;
private float FillSpeed = 0.5f;
public GameObject UIHolder;
public GameObject StartForm;
public InputField NameInput;
public GameObject ArmyUI;
public GameObject InvestUI;
public GameObject LaboratoryUI;
private float time = 0.0f;
public float IncomePerdiod = 5f;
public bool GameStart = true;
//Invest Prices
//Market
public int marketprice;
public int marketlevel;
public Text marketpricetext;
public Text markettext;
//Taxes
public int taxesprice;
public int taxeslevel;
public Text taxespricetext;
public Text taxestext;
void Start()
{
Load();
if(GameStart == true)
{
gold = 100;
armypower = 0;
Level = 1;
LevelProgress = 0;
//Laboratory
discount = 1;
Income = 1;
incomeboost = 1;
//Invest
marketlevel = 0;
taxeslevel = 0;
ArmyUIHide();
InvestUIHide();
ButtonHide();
UIHide();
StartForm.SetActive(true);
Save();
}
if (GameStart == false)
{
StartForm.SetActive(false);
ArmyUIHide();
InvestUIHide();
ButtonShow();
UIShow();
Save();
}
}
void Update()
{
if(time >= IncomePerdiod)
{
time = 0.0f;
gold += Income * incomeboost;
}
time += Time.deltaTime;
Save();
GoldText.text = "Gold: " + gold;
ArmyPowerText.text = "Army Power: " + armypower;
PlayerNametext.text = PlayerName + " LVL " + Level;
//Market
markettext.text = "Market LVL " + marketlevel;
marketpricetext.text = marketprice.ToString();
marketprice = 50 * discount;
//Taxes
taxestext.text = "Taxes LVL " + taxeslevel;
taxespricetext.text = taxesprice.ToString();
taxesprice = 250 * discount;
if (LevelBar.value < LevelProgress)
{
LevelBar.value += FillSpeed * Time.deltaTime;
}
if (LevelBar.value > LevelProgress)
{
LevelBar.value = LevelProgress;
}
if (LevelProgress >= 1)
{
Level++;
LevelProgress = 0;
}
}
public void Save()
{
//UI
PlayerPrefs.SetString("gold", gold.ToString());
PlayerPrefs.SetString("armypower", armypower.ToString());
PlayerPrefs.SetString("GameStart", GameStart.ToString());
PlayerPrefs.SetString("PlayerName", PlayerName.ToString());
PlayerPrefs.SetString("Level", Level.ToString());
PlayerPrefs.SetString("LevelProgress", LevelProgress.ToString());
//Laboratory
PlayerPrefs.SetString("discount", discount.ToString());
PlayerPrefs.SetString("Income", Income.ToString());
PlayerPrefs.SetString("incomeboost", incomeboost.ToString());
//Invest
PlayerPrefs.SetString("marketlevel", marketlevel.ToString());
PlayerPrefs.SetString("taxeslevel", taxeslevel.ToString());
}
public void Load()
{
//UI
gold = int.Parse(PlayerPrefs.GetString("gold", "100"));
armypower = int.Parse(PlayerPrefs.GetString("armypower", "0"));
GameStart = bool.Parse(PlayerPrefs.GetString("GameStart", "true"));
PlayerName = PlayerPrefs.GetString("PlayerName", "Guest");
Level = int.Parse(PlayerPrefs.GetString("Level", "1"));
LevelProgress = int.Parse(PlayerPrefs.GetString("LevelProgress", "0"));
//Laboratory
discount = int.Parse(PlayerPrefs.GetString("discount", "1"));
Income = int.Parse(PlayerPrefs.GetString("Income", "1"));
incomeboost = int.Parse(PlayerPrefs.GetString("incomeboost", "1"));
//Invest
marketlevel = int.Parse(PlayerPrefs.GetString("marketlevel", "50"));
taxeslevel = int.Parse(PlayerPrefs.GetString("taxeslevel", "250"));
}
public void ButtonHide()
{
ButtonHolder.SetActive(false);
}
public void ButtonShow()
{
ButtonHolder.SetActive(true);
}
public void UIHide()
{
UIHolder.SetActive(false);
}
public void UIShow()
{
UIHolder.SetActive(true);
}
public void ArmyUIShow()
{
ArmyUI.SetActive(true);
}
public void ArmyUIHide()
{
ArmyUI.SetActive(false);
}
public void LaboratoryUIShow()
{
LaboratoryUI.SetActive(true);
}
public void LaboratoryUIHide()
{
LaboratoryUI.SetActive(false);
}
public void InvestUIShow()
{
InvestUI.SetActive(true);
}
public void InvestUIHide()
{
InvestUI.SetActive(false);
}
public void EnterName()
{
PlayerName = NameInput.text;
StartForm.SetActive(false);
UIShow();
GameStart = false;
ButtonShow();
}
public void ArmyClick()
{
ArmyUIShow();
ButtonHide();
LaboratoryUIHide();
InvestUIHide();
}
public void InvestClick()
{
InvestUIShow();
ButtonHide();
LaboratoryUIHide();
ArmyUIHide();
}
public void LaboratoryClick()
{
ArmyUIHide();
ButtonHide();
LaboratoryUIShow();
InvestUIHide();
}
public void Home()
{
ArmyUIHide();
InvestUIHide();
ButtonShow();
LaboratoryUIHide();
}
//Buy Invest
public void BuyMarket()
{
if(gold >= marketprice && marketlevel < 5)
{
marketlevel++;
Income += 1;
gold -= marketprice;
if (LevelProgress < 1f - 0.05f)
{
LevelProgress += 0.15f;
}
else
{
lvlremainder += (LevelProgress + 0.05f - 1f);
Level++;
LevelProgress = 0;
LevelProgress += lvlremainder;
}
}
}
public void BuyTaxes()
{
if (gold >= taxesprice && taxeslevel < 10)
{
taxeslevel++;
Income += 3;
gold -= taxesprice;
if (LevelProgress < 1f - 0.15f)
{
LevelProgress += 0.15f;
}
else
{
lvlremainder += (LevelProgress + 0.15f - 1f);
Level++;
LevelProgress = 0;
LevelProgress += lvlremainder;
}
}
}
The saving system in your code is not developed correctly, remove the Save() method call from the Start and Update methods, and call it only when the necessary event occurs, such as the end of the game.
Do local prop like this to save data:
private int Gold
{
get => PlayerPrefs.GetInt("gold");
set => PlayerPrefs.SetInt("gold", value);
}
I found the fault I parsed as int for a float that messed the whole saving system up!

How can I create a method polymorphism?

I learn programming oriented object, and I want to do this code but I got those problems, I think I don't really know the concept of virtual.
View image 1
Code:
class Joueur
{
private string _nom;
private string _prenom;
private DateTime _dateDeNaissance;
private string _position;
private bool _reserve;
public string nom
{
get { return _nom; }
set { _nom = value; }
}
public string prenom
{
get { return _prenom; }
set { _prenom = value; }
}
public DateTime dateDeNaissance
{
get { return _dateDeNaissance; }
set { _dateDeNaissance = value; }
}
public string position
{
get { return _position; }
set { _position = value; }
}
public bool reserve
{
get { return _reserve; }
set { _reserve = value; }
}
public Joueur()
{
}
public Joueur(string nom, string prenom, DateTime dateDeNaissance, string position, bool reserve)
{
this.nom = nom;
this.prenom = prenom;
this.dateDeNaissance = dateDeNaissance;
this.position = position;
this.reserve = reserve;
}
public double virtual CalculerPrime(int joues, int gagnes)
{
double Prime;
if (reserve == false)
Prime = 10000 * (gagnes / joues);
else
Prime = ((10000 * (gagnes / joues)) / 2);
return Prime;
}
}
You need to do like this in your code:
public virtual double CalculerPrime(int joues, int gagnes)
{
double Prime;
if (reserve == false)
Prime = 10000 * (gagnes / joues);
else
Prime = ((10000 * (gagnes / joues)) / 2);
return Prime;
}

How to access array members that are of an interface type c#

I am a little lost here.
Basically, I need to access an array item, a string and display it. Here is the code.
namespace Test3_2_Practice
{
public partial class InterfaceImplementation : Form
{
//Array
ICombatant[] combatants = new ICombatant[2];
public InterfaceImplementation()
{
InitializeComponent();
}
private void btnTest_Click(object sender, EventArgs e)
{
combatants[0] = new PlayerCharacter ("Conan" , 500);
combatants[1] = new MonsterCharacter ("Bob" , 5);
combatants[2] = new MonsterCharacter ("Snake" , 15);
string output = "Fighters" + Environment.NewLine;
for (var i = 0; i < combatants.Length; i++)
{
var character = combatants[i];
output += "Character:" + combatants[i].
}
}
}
}
So I have my array, combatants composed of two types of instances. I want to access the name, "Conan" and add it to a string for output. How do I go about doing that? Here is the rest of the code if that helps. Thanks!
namespace Test3_2_Practice
{
//Interface
interface ICombatant
{
int TakeDamage(int damageAmount);
string GetHealthDisplay();
}
class PlayerCharacter : ICombatant
{
private string characterName;
private int currentHealth;
private int maxHealth;
public string CharacterName
{
get { return characterName; }
set { characterName = value; }
}
public int CurrentHealth
{
get { return currentHealth; }
set { currentHealth = value; }
}
public int MaxHealth
{
get { return maxHealth; }
set { maxHealth = value; }
}
public PlayerCharacter(string characterName, int maxHealth)
{
CharacterName = characterName;
CurrentHealth = MaxHealth = maxHealth;
}
//Damage Class
public int TakeDamage(int damageAmount)
{
if (damageAmount > currentHealth)
{
damageAmount = currentHealth;
return damageAmount;
}
else
{
currentHealth = currentHealth - damageAmount;
return damageAmount;
}
}
//Health Class
public string GetHealthDisplay()
{
return ("Health " + CurrentHealth.ToString() + "/" + MaxHealth).ToString();
}
}
class MonsterCharacter : ICombatant
{
private string monsterName;
private int health;
public string MonsterName
{
get { return monsterName; }
set { monsterName = value; }
}
public int Health
{
get { return health; }
set { health = value; }
}
public MonsterCharacter(string monsterName, int health)
{
MonsterName = monsterName;
Health = health;
}
//Damage Class
public int TakeDamage(int damageAmount)
{
if(damageAmount > health)
{
damageAmount = health;
return damageAmount;
}
else
{
health = health - damageAmount;
return damageAmount;
}
}
//Health Class
public string GetHealthDisplay()
{
return "Health " + Health;
}
}
}
Actually, since name is common to all implementer of interface that Name property should be included to interface itself like
interface ICombatant
{
int TakeDamage(int damageAmount);
string GetHealthDisplay();
public string CharacterName
{
get;
set;
}
}
In your current scenario, you will have to cast it specific concrete type before accessing it
var character = combatants[i];
if(character is PlayerCharacter)
output += "Character:" + ((PlayerCharacter)character).CharacterName;
It's better to make a good use of your interface. Create a new method in your interface:
interface ICombatant
{
int TakeDamage(int damageAmount);
string GetHealthDisplay();
string GetCombatantName(); // added this
}
Then implement in both classes which implements it:
class PlayerCharacter : ICombatant
{
// ... a lot of code ...
public string GetCombatantName()
{
return String.Format("Character: {0}", this.CharacterName);
}
}
class MonsterCharacter: ICombatant
{
// ... a lot of code ...
public string GetCombatantName()
{
return String.Format("Monster: {0}", this.MonsterName);
}
}
And use it like this:
private void btnTest_Click(object sender, EventArgs e)
{
combatants[0] = new PlayerCharacter("Conan", 500);
combatants[1] = new MonsterCharacter("Bob", 5);
combatants[2] = new MonsterCharacter("Snake", 15);
string output = "Fighters" + Environment.NewLine;
foreach (var combatant in combatants)
{
output += combatant.GetCombatantName();
}
}
So if one day you get ten distinct types of ICombatant (like AnimalCharacter, VirusCharacter), you don't have to nest a lot of ifs to check and cast types to get the proper property.
Interfaces are meant exactly to avoid this kind of stuff, hiding the implementation details.

C# struct treated like a Reference

I have struct called KinectBody and i assign an object of this type which is in a class called BodyContributor to a field in a class called KinectSystemComponent. Now every time the field in the BodyContributor changes, the field in my KinectSystemComponent changes too just like it was a reference type. How does that happen. Below are the involved classes:
using System.Collections.Generic;
namespace Alpaca.Systems.Kinect
{
public class KinectSystemComponent : KinectComponent, Alpaca.Systems.Events.IEventHandler<UserTrackingEvent>
{
private static Dictionary<ulong, KinectBody> assignedBodies = new Dictionary<ulong, KinectBody>();
BodyContributor contributor;
public static bool useSystemComponents = true;
public static bool IsBodyAlreadyAssigned(KinectBody body)
{
return assignedBodies.ContainsKey(body.trackingID);
}
public override void Initialize()
{
contributor = BodySource.Instance.contributor;
this.ComponentType = ComponentType.SystemComponent;
}
public override void OnUpdate()
{
//if(useSystemComponents)
//{
// if(!Body.isTracked)
// {
// KinectBody[] bodies = contributor.GetBodies();
// for(int i = 0; i < bodies.Length; i++)
// {
// KinectBody currentBody = bodies[i];
// if(currentBody.isTracked)
// {
// if(!IsBodyAlreadyAssigned(currentBody))
// {
// this.Body = currentBody;
// }
// }
// }
// }
// else
// {
// Body = contributor.GetBodyByID(Body.trackingID);
// }
//}
//else if(Body.isTracked)
//{
// Body = new KinectBody();
//}
}
public void HandleEvent(UserTrackingEvent #event)
{
Body = contributor.GetBodyByID(#event.id);
}
}
}
using System;
using Alpaca.Systems;
namespace Alpaca.Systems.Kinect
{
public abstract class KinectComponent : AlpacaComponent
{
private KinectBody body;
public KinectBody Body
{
get
{
return body;
}
protected set
{
body = value;
}
}
}
}
using System.Collections.Generic;
using UnityEngine;
using Windows.Kinect;
using Alpaca.Utility;
namespace Alpaca.Systems.Kinect
{
public struct KinectBody
{
public Dictionary<JointType, KinectJoint> joints;
Dictionary<JointType, Windows.Kinect.Joint> rawJoints;
public bool isTracked;
public ulong trackingID;
public Vector3 position;
public Vector3 hipsDirection;
public Vector3 shouldersDirection;
public float bodyTurnAngle;
public Vector3 leftArmDirection;
public Vector3 rightArmDirection;
public void Init()
{
joints = new Dictionary<JointType, KinectJoint>(25, new JointTypeComparer());
}
public void RefineBody(Body body)
{
this.rawJoints = body.Joints;
for(int i = 0; i < 25; i++)
{
joints[KinectHelper.GetJointAtIndex(i)] = new KinectJoint(rawJoints[KinectHelper.GetJointAtIndex(i)]);
}
position = joints[JointType.SpineMid].position;
isTracked = body.IsTracked;
trackingID = body.TrackingId;
hipsDirection = Vector3.zero;
shouldersDirection = Vector3.zero;
bodyTurnAngle = 0;
leftArmDirection = Vector3.zero;
rightArmDirection = Vector3.zero;
}
public void ModifyJoint(JointType type, KinectJoint joint)
{
joints[type] = joint;
}
}
public class KinectBodyComparer : IEqualityComparer<KinectBody>
{
bool System.Collections.Generic.IEqualityComparer<KinectBody>.Equals(KinectBody x, KinectBody y)
{
return x.trackingID == y.trackingID;
}
int System.Collections.Generic.IEqualityComparer<KinectBody>.GetHashCode(KinectBody obj)
{
return unchecked((int) (obj.trackingID % 1000));
}
}
}
namespace Alpaca.Systems.Kinect
{
public abstract class BodyContributor
{
protected KinectBody[] bodies = new KinectBody[6];
private BodyRefiner refiner;
protected KinectBody emptyBody;
public BodyContributor(BodyRefiner refiner)
{
this.refiner = refiner;
emptyBody = new KinectBody();
}
public void Update()
{
this.bodies = refiner.GetBodies();
UpdateBodies();
}
protected abstract void UpdateBodies();
public KinectBody GetBodyByID(ulong id)
{
for(int i = 0; i < bodies.Length; i++)
{
KinectBody curBody = bodies[i];
if(curBody.trackingID == id)
{
return curBody;
}
}
return emptyBody;
}
public KinectBody[] GetBodies()
{
return bodies;
}
}
}
As Carsten has pointed out, KinectComponent is a class, a reference type.
KinectBody may be a value type, but it contains reference types (Dictionarys), which I suspect will lead to behaviour you don't expect.
Your struct also is large and mutable, which breaks these useful design guidelines

Categories

Resources