Multiple games with same rpg-classes base Please, if you find a better title, tell me because I really didn't know what to put in here!

I like to guess how I could implement a game, and to be sure that my perception is working, I plan to develop small games. For instance, I plan to build a FFTA-like game, a FTL-like game, a tower-defense game, and so on. As a base, I created a bunch of rpg-classes (Warrior, Wizard, ...), and I want to use all of them in each of my futur game.

The only sure thing I know about what should be in common for every game is the stat system:  


  
  Strength  
  Constitution  
  Agility  
  Accuracy  
  Intelligence  
  Charisma  
  Luck  
  


I will continue with what I plan to do, but I am not really sure about my choices:

namespace General
{
    public interface IStatsOwner
    {
        int Strength { get; }
        int Constitution { get; }
        int Agility { get; }
        int Accuracy { get; }
        int Intelligence { get; }
        int Charisma { get; }
        int Luck { get; }
    }

    public abstract class WarriorBase : IStatsOwner
    {
        public int Strength { get { return 5; } }
        public int Constitution { get { return 4; } }
        public int Agility { get { return 3; } }
        public int Accuracy { get { return 3; } }
        public int Intelligence { get { return 2; } }
        public int Charisma { get { return 3; } }
        public int Luck { get { return 1; } }
    }

    public abstract class WizardBase : IStatsOwner
    {
        public int Strength { get { return 2; } }
        public int Constitution { get { return 3; } }
        public int Agility { get { return 3; } }
        public int Accuracy { get { return 3; } }
        public int Intelligence { get { return 5; } }
        public int Charisma { get { return 4; } }
        public int Luck { get { return 1; } }
    }

    public interface IGame
    {
        Type WarriorType { get; }
        Type WizardType { get; }
    }
}




namespace FFTALike
{
    public interface IDamageable
    {
        void TakeDamages();
    }

    public class Warrior : General.WarriorBase, IDamageable
    { 
        public void TakeDamages() { Console.WriteLine($"Taking damages with CON={Constitution}"); }
    }

    public class Wizard : General.WizardBase, IDamageable
    {
        public void TakeDamages() { Console.WriteLine($"Taking damages with CON={Constitution}"); }
    }

    public Game : IGame
    {
        public Type WarriorType { get { return typeof(Warrior); } }
        public Type WizardType { get { return typeof(Wizard); } }
    }
}




namespace TowerDefense
{
    public interface ILocatable
    {
        Location Location { get; set; } // Location(int X, int Y)
    }

    public interface IShooter
    {
        void Shoot();
    }

    public class Warrior : WarriorBase, ILocatable, IShooter
    {
        public Location Location { get; set; }
        public void Shoot() { Console.WriteLine($"Shooting with STR={Strength}"); }
    }

    public class Wizard : WizardBase, ILocatable, IShooter
    {
        public Location Location { get; set; }
        public void Shoot() { Console.WriteLine($"Shooting with STR={Strength}"); }
    }

    public Game : IGame
    {
        public Type WarriorType { get { return typeof(Warrior); } }
        public Type WizardType { get { return typeof(Wizard); } }
    }
}




What I feel uncomfortable with is:  


  
  I don't enforce the fact that IGame.WarriorType inherits WarriorBase  
  I duplicate TakeDamages(), Location { get; set; } and Shoot() (I actually have 20 rpg-classes, so I isocuplate these implementations)  
  


Please note that I want the final implementation to fit with SOLID principles, because I want these projects to be training projects.
I'm having trouble visualizing what your intention here is.

Let's talk about your code itself, then I'll move on to a slight redesign:

C# has a nice-new language feature called "Expression-Bodied members", basically, this:

public int Strength =&gt; 5;


That's equivalent, but much shorter than your version:


public int Strength { get { return 5; } }



So, I would recommend moving to something like that, if you want to keep this design.

But, I would redesign this:

Everything has the same stats, right? They all need the stats, they just might have different values?

public class Stats
{
    public int Strength { get; }
    public int Constitution { get; }
    public int Agility { get; }
    public int Accuracy { get; }
    public int Intelligence { get; }
    public int Charisma { get; }
    public int Luck { get; }
}


So we're using another new language feature here: auto-implemented readonly properties. Basically: don't put code in the get, and these can only be set by the constructor. Of course, we need a constructor:

public Stats(int strength, int constitution, int agility, int accuracy, int intelligence, int charisma, int luck)
{
    Strength = strength;
    Constitution = constitution;
    Agility = agility;
    Accuracy = accuracy;
    Intelligence = intelligence;
    Charisma = charisma;
    Luck = luck;
}


Now, because everyone has their own specific stats, but they all follow the same structure, we use this class in the object:

public abstract class Entity
{
    public Stats Stats { get; }
    public Entity(Stats stats)
    {
        Stats = stats;
    }
}


So, because everybody is the same type of "Entity" with different attributes, we then move on to the Warrior / Wizard / etc.:

public abstract class Warrior : Entity
{
    public Warrior()
        : base(new Stats(5, 4, 3, 3, 2, 3, 1))
    { }
}

public abstract class Wizard : Entity
{
    public Wizard()
        : base(new Stats(2, 3, 3, 3, 5, 4, 1))
    { }
}


Now you can use the ILocatable etc. in the specific games, but the basics are all the same across-the-board. We build a more extracted model: the things that have to be there are, and are enforced.

With this part:


public interface IGame
{
    Type WarriorType { get; }
    Type WizardType { get; }
}



We can do some trickery:

public abstract class Game&lt;TWarrior, TWizard&gt;
    where TWarrior : Warrior
    where TWizard : Wizard
{
    public Type WarriorType =&gt; typeof(TWarrior);
    public Type WizardType =&gt; typeof(TWizard);
}


Now, I don't know why you need the WarriorType or WizardType, you probably don't need them in this version, because we put those types on the Game class.

We obviously haven't handled the TakeDamages or Shoot, right? We need to find a way to deal with that on a game-by-game basis: enter ECS (entity-component-system).

We built the entities, they have stats (components), now we need "systems" to handle the components.

public class ActualWizard : Wizard { }
public class ActualWarrior : Warrior { }

public class ActualGame : Game&lt;ActualWizard, ActualWarrior&gt;
{
    public class ShootSystem
    {
        public static void Shoot(Entity source, Entity target)
        {
            Console.WriteLine($"Shooting with STR={source.Stats.Strength}");
        }
    }
}


So, now you would call ShootSystem.Shoot(myWarrior, myWizard);, which would be where you handle all of the logic to actually shoot the wizard with the warrior. This also means that all entities can use the same function, whatever they may be. (Paladin, etc.)

var myGame = new ActualGame();
var myWarrior = new ActualWarrior();
var myWizard = new ActualWizard();
myGame.ShootSystem.Shoot(myWarrior, myWizard);


You would add any stats specific to warriors to ActualWarrior, same with ActualWizard, but you don't put the logic there, you build a System for it.