Hey. Read through the topic, and I have to say, that sounds like a neat project, and definitely a good learning experience. Don't let anyone tell you otherwise, C# is a perfectly fine language to use. I know C and C++ *very* well, and if I were writing a roguelike, I'd be using C#.
As for putting in my 2 cents on advice, I'm gonna have to mirror Meltdown and Blacken; using inheritance is *Awesome*, and you will be a much better programmer once you see how often you can use it.
I mean, think about it this way. This is an example from a 3d game engine I wrote (in C++, but I'll use C# terms) a few years ago with Meltdown. We had a world full of a bunch of stuff, and a scripting language that wanted to be able to mess with it all. We had *one* base class, Object, that implemented just enough to hook it into the scripts. From that, we had all sorts of things - vectors, timers, etc. Things we wanted to script, but weren't in the world.
One of the child classes of Object was WorldObject. This added a position and velocity, and the networking code to syncronize it, etc. Nothing else. This is still an abstract class; it can't actually be used yet. Off of WorldObject we wrote Mesh, Billboard, ParticleSystem, RigidBody, etc. Most of the game objects. Eventually, down the line, we'd have things like characters and weapons and pickups.
But in none of those base classes did we ever have to worry about storing the position of the object, or how it was handled on the network. By using inheritance, we were able to write all that logic exactly once.
I'd go so far as to say that if you're ever copy-and-pasting anything, you could use Inheritance and make your life easier. Suppose you have your Monster and Projectile classes. (Off of those you might have, say, Goblin and MagicMissile.) Each of those might have a Think() function, in which it does things like AI or movement or whatever. Each sub-class would reimplement that to do it's specific behavior. In your main loop, you'd have to say something like:
foreach (Monster m in myMonsters) { m.Think() }
foreach (Projectile p in myProjectiles) { p.Think() }
This is a perfect place for an Interface, IThinkable:
public interface IThinkable {
public void Think();
}
public class Monster : IThinkable
{ ... }
public class Projectile : IThinkable
{ ... }
And then in your main loop part:
foreach(IThinkable thinker in myThinkables) { thinker.Think() }
And the benefit is, if you ever want to add something else that thinks - say, next month you decide that items on the floor really should be able to Think, if they want to, say, degrade from exposure to the elements - then you can just inherit from IThinkable, and all your old code will automatically work with the new classes. It gets rid of the massive potential for bugs or missing one little copy.
You can never implement too many interfaces. Whenever you find yourself doing anything the same way in multiple classes, you'll probably want to make an interface for it.