0

I'm programming a dungeon generator for a roguelike. I've a base class called Room. It contains methods that can be inherited by other types of rooms. It looks like this but then a little more advanced

class Room
{
   protected virtual void Construct() { /*make square room here*/ }
}
class RoundRoom : Room
{
   protected override void Construct() { /*make round room here*/ }
}

My class that generates rooms needs to be "fed" with rooms to generate. The room handles the construction, and I've different types of rooms. And I want it to have it that certain specific rooms can be generated based on some conditions or chances.

So I feed it with different types of rooms. First I thought of this:

    class RoomEntry
    {
        public Point chance;
        public Room room;
    }

And then have an array of it

RoomEntry[] entries;

And then just feed it

Generator.Feed(entries[random.Next(0, 10)].room); // just an example

But that won't work! If I edit the room in the generator, It'll change in RoomEntry too! And I need to use it quite a few times! So if I would make new rooms based on some room type... It'll work!

So I came up with this:

class RoomPlanner
{
    class RoomEntry<T> where T : Room, new()
    {
        public Point chance;
        T r;

        public Room RoomToBuild()
        {
            return new T();
        }
    }

    RoomEntry<Room>[] entrys;

    public void Foo()
    {
        entrys = new RoomEntry<Room>[10];

        for (int i = 0; i < entrys.Length; i++)
        {
            entrys[i] = new RoomEntry<RoundRoom>();
        }
    }
}

But that's not possible. I'm getting this error:

Cannot implicitly convert type 'Super_ForeverAloneInThaDungeon.RoomPlanner.RoomEntry<Super_ForeverAloneInThaDungeon.RoundRoom>' to 'Super_ForeverAloneInThaDungeon.RoomPlanner.RoomEntry<Super_ForeverAloneInThaDungeon.Room>'

So, how do can I make it accept classes that inherit from Room, or how do I take a different approach to this problem?

It's not an duplicate of this. That's a different problem, and I do not have enough information to fix my problem entirely out of it.

Community
  • 1
  • 1
joppiesaus
  • 5,471
  • 3
  • 26
  • 36

2 Answers2

3

The problem is that covariant/contravariant type parameters can only be used with interface or delegate types. (More information on that in this MSDN article.) Essentially, there is no way to declare a RoomEntry<T> that is contravariant with RoomEntry<Room>, even with the constraint that T : room.

You could get around this by defining an IRoomEntry interface that is implemented by RoomEntry<T>, like this:

interface IRoomEntry
{
    Room RoomToBuild();
}

class RoomPlanner
{
    class RoomEntry<T> : IRoomEntry
        where T : Room, new()
    {
        public Point chance;
        T r;

        public Room RoomToBuild()
        {
            return new T();
        }
    }

    IRoomEntry[] entrys;

    public void Foo()
    {
        entrys = new IRoomEntry[10];

        for (int i = 0; i < entrys.Length; i++)
        {
            entrys[i] = new RoomEntry<RoundRoom>();
        }
    }
}
BJ Myers
  • 6,617
  • 6
  • 34
  • 50
  • That works! The only thing that breaks is the variables. `IRoomEntry` doesn't have `Point chance`, so you have to cast it, but that's impossible. Make `Point chance` a property instead will work. – joppiesaus Mar 23 '15 at 20:03
1

Seems like you just want to Clone the room before feeding it to the Generator. You could just add a Clone method to your Room class:

Room Clone() { return (Room)this.MemberwiseClone(); }

And then feed it like so:

Generator.Feed(entries[random.Next(0, 10)].room.Clone()); 
Luaan
  • 62,244
  • 7
  • 97
  • 116
  • I like your approach, it's not a direct solution to the problem, but it may be actually a better one. – joppiesaus Mar 23 '15 at 17:04
  • Be careful with too many objects with just copying stuff around. It would be better to try to use some Value Types if you go towards cloning. – George Onofrei Mar 23 '15 at 17:08
  • 2
    @GeorgeOnofrei Of course, this also depends on how the values are actually used. I assume this is only used while generating the "dungeon", so there is a fixed amount of rooms being created, and those rooms are there to stay. Value-types aren't a silver bullet either - you really only want to use them for "a bunch of values" kind of objects; implementing interfaces is tricky (you just lost that "not on the heap, maybe" part), you lose inheritance, and it's a pretty good idea to avoid mutable structs as well. – Luaan Mar 24 '15 at 07:14