IMHO, you should make a distinction between "tiles" (the underlying basemap) and "objects" (things the player can interact with, like doors that open, dragons that attack, or pits that kill).
If you want to compare this with a 3D-video game, the "tiles" would be the environment you cannot interact with, and the "objects" would be the clickable things.
The mere tiles can be instances of one single class and hold the information relevant and common to all tiles, like rendering hints (which character in which colour) or movement aspects (can be passed, movement speed, etc).
The objects are then placed on top of the tiles.
Imagine you have a map with a lot of floor and walls, and at two positions you have two doors. All "tiles" behave the same (you can walk on floor, no matter which floor tile) but you will butt your head against a wall (no matter where the wall is). But the doors are different: One door requires the "Green Key" and the other door requires the "Embroidered Key of the Dissident Pixie".
This difference is where your if
-issue arises. The door needs extra information. In order to define a map, you need all tiles (identical within each class) and another lists of objects placed on certain tiles (each object different).
Doors, Wells, Dragons, Switches etc could inherit from a common base class which implements standard actions like "inspect", "interact", "attack", "yell at", and maybe special interfaces for special actions.
So a complete game definition could look like this:
game = {'baseMap': '#here comes your 2D array of tiles',
'objects': [ {'class': Door, 'position': (x, y), 'other Door arguments': ...},
{'class': Door, 'position': (x2, y2), 'other Door arguments': ...},
{'class': Dragon, 'position': (x3, y3), 'dragon arguments': ...}, ] }
Then for instantiating the actual objects (object in the OO sense, not in the game sense), walk throught this definition, and call the c'tor of each object with the dictionary items as keyword-arguments (double-asterisk). This is only one possible approach of many.
For rendering, display the tile presentation if the tile is empty, or the object representation if there is an object on the tile.
This is what I mean with double-asterisk:
class Door:
def __init__ (self, position, colour, creaking = True):
print (position, colour, creaking)
objDefs = [...,
{'class': Door, 'kwargs': {'position': (2, 3), 'colour': 'black'} },
...]
#Here you actually iterate over objDefs
objDef = objDefs [1]
obj = objDef ['class'] (**objDef ['kwargs'] )
Big Edit:
This is an idea of how one could implement the rendering of the map with both tiles and objects. (Just my two cents):
#! /usr/bin/python3.2
colours = {'white': 7, 'green': 2, 'blue': 4, 'black': 0, 'yellow': 3}
class Tile:
data = {'.': ('Floor', 'white', True),
'Y': ('Forest', 'green', False),
'~': ('Water', 'blue', False) }
def __init__ (self, code, position):
self.code = code
self.position = position
self.name, self.colour, self.passable = Tile.data [code]
def __str__ (self):
return '\x1b[{}m{}'.format (30 + colours [self.colour], self.code)
class GameObject:
#here got he general interfaces common to all game objects
def __str__ (self):
return '\x1b[{}m{}'.format (30 + colours [self.colour], self.code)
class Door (GameObject):
def __init__ (self, code, position, colour, key):
self.code = code
self.position = position
self.colour = colour
self.key = key
def close (self): pass
#door specific interface
class Dragon (GameObject):
def __init__ (self, code, position, colour, stats):
self.code = code
self.position = position
self.colour = colour
self.stats = stats
def bugger (self): pass
#dragon specific interface
class Map:
def __init__ (self, codeMap, objects):
self.tiles = [ [Tile (c, (x, y) ) for x, c in enumerate (line) ] for y, line in enumerate (codeMap) ]
self.objects = {obj ['args'] ['position']: obj ['cls'] (**obj ['args'] ) for obj in objects}
def __str__ (self):
return '\n'.join (
''.join (str (self.objects [ (x, y) ] if (x, y) in self.objects else tile)
for x, tile in enumerate (line) )
for y, line in enumerate (self.tiles)
) + '\n\x1b[0m'
mapRouge = ['~~~~~~~~~~',
'~~~~.....Y',
'YYYYY.YYYY',
'YYYY....YY']
objects = [ {'cls': Door,
'args': {'code': '.', 'position': (5, 2), 'colour': 'black',
'key': 'Ancient Key of Constipation'} },
{'cls': Dragon,
'args': {'code': '@', 'position': (7, 3), 'colour': 'yellow',
'stats': {'ATK': 20, 'DEF': 20} } } ]
theMap = Map (mapRouge, objects)
print (theMap)
And this is the result: