// #include #include "mem.h" #include "image.h" #include "tilemap.h" #include "dynar.h" #include "monolog.h" /* Hide tiles for debuggging, or not. */ #ifdef ERUTA_NOGFX_MODE #define TILEMAP_NO_TILES #endif /** A tile map is a game map, roughly equivalent to a "level" that uses tiled * panes for it's display. It also contains an Area for the physical, logical * and graphical representation of in-game objects. * The plan is to have an area per tile map and copy the player characters between * those areas when moved, but keeping their ID's the same. * This should make keeping track of the characters (from scripting, etc) easier. */ struct Tilemap_ { Image * texture; int textureid; int gridw; int gridh; Tileset * set; Dynar * panes; Area * area; }; #define TEXTURE_TILE "tile" #define TEXTURE_PLAYER "play" #define TEXTURE_GEAR "gear" #define TEXTURE_WEAPON "arms" #define TEXTURE_FOE "foes" #define TEXTURE_ITEM "item" /** Loads an image of the given category and index as a texture. */ Image * image_loadtexture(const char * category, int index) { char buf[1024]; Image * image; sprintf(buf, "data/image/%s_%03d.png", category, index); image = image_load(buf); return image; } /** Cleans up a tilemap. */ Tilemap * tilemap_done(Tilemap * self) { int index; if(!self) return NULL; for(index = 0; index < TILEMAP_PANES; index++) { tilepane_free(dynar_getptr(self->panes, index)); } dynar_free(self->panes); tileset_free(self->set); /* the area is not owned so we don't free that */ // tilemap_initempty(self); return self; } /** Initializes a tile map */ Tilemap* tilemap_init(Tilemap * self, Tileset * set, int w, int h, Area * unused__) { int index; (void) unused__; if(!self) return NULL; self->gridw = w; self->gridh = h; self->set = set; if(!self->set) { return NULL; } self->panes = dynar_new(TILEMAP_PANES, sizeof(Tilepane *)); if(!self->panes) { return NULL; } for(index = 0; index < TILEMAP_PANES; index++) { dynar_putptr(self->panes, index, NULL); } return self; } /** Frees the tile map and initializes it. */ Tilemap * tilemap_free(Tilemap * map) { tilemap_done(map); mem_free(map); return NULL; } /** Allocates a new tile map and initializes it. */ Tilemap * tilemap_new(Tileset * set, int w, int h, Area * unused__ ) { Tilemap * map = STRUCT_ALLOC(Tilemap); (void) unused__; if(!tilemap_init(map, set, w, h, NULL)) { return tilemap_free(map); } return map; } /** Returns a pointer to the pane at index or NULL if out of range. */ Tilepane * tilemap_pane(Tilemap * self, int index) { return dynar_getptr(self->panes, index); } /** Sets a new tile pane for the pane at indexeth index of the tile map. * the old pane, if any, will be deleted with tilepane_free */ Tilepane * tilemap_pane_(Tilemap * self, int index, Tilepane * pane) { Tilepane * oldpane; oldpane = tilemap_pane(self, index); // Replace old pane, so free it. tilepane_free(oldpane); dynar_putptr(self->panes, index, pane); return pane; } /** Makes a new tile pane for the pane at indexeth index of the tile map. */ Tilepane * tilemap_panenew(Tilemap * self, int index, int w, int h) { Tilepane * pane = tilepane_new(self->set, w, h); return tilemap_pane_(self, index, pane); } /** Returns the tile in the tile map in the given layer at the given tile coords. */ Tile * tilemap_get(Tilemap * self, int l, int x, int y) { Tilepane * pane = tilemap_pane(self, l); if(!pane) return NULL; return tilepane_get(pane, x, y); } /** Converts the tile's flags to an integer id for the best Thing to represent this tile. Returns THING_UNUSED if it needs no physical representation. */ int tile_thingkind(Tile * tile) { if (!tile) return THING_UNUSED; if (tile_index(tile)<1) return THING_UNUSED; if (tile_isflag(tile, TILE_WALL)) return THING_WALL; if (tile_isflag(tile, TILE_STAIR)) return THING_STAIR; if (tile_isflag(tile, TILE_WATER)) return THING_WATER; return THING_UNUSED; } /** Sets a tile in the tile map to the given tile. */ Tile * tilemap_settile(Tilemap * self, int l, int x, int y, Tile * tile) { Tile * aidt = NULL; Tilepane * pane = tilemap_pane(self, l); if(!pane) return NULL; aidt = tilepane_set(pane, x, y, tile); if(!aidt) return NULL; return tile; } /** Sets a tile in the tile map to the tile with the given index. */ Tile * tilemap_setindex(Tilemap * self, int l, int x, int y, int index) { Tilepane * pane = tilemap_pane(self, l); Tile * tile = NULL; if(!pane) return NULL; tile = tilepane_setindex(pane, x, y, index); if(!tile) return NULL; return tile; } /** Sets a rectangle area in the tile map to the given tile. */ Tile * tilemap_rect(Tilemap * self, int l, int x, int y, int w, int h, Tile * tile) { Tilepane * pane = tilemap_pane(self, l); if(!pane) return NULL; return tilepane_rect(pane, x, y, w, h, tile); } /** Sets a rectangle area in the tile map to the given tile. */ Tile * tilemap_fill(Tilemap * self, int l, Tile * tile) { Tilepane * pane = tilemap_pane(self, l); return tilepane_fill(pane, tile); } /** Gets the index of the tile at the given location in the tilemap. */ int tilemap_getindex(Tilemap * self, int l, int x, int y) { Tilepane * pane = tilemap_pane(self, l); return (int) tilepane_getindex(pane, x, y); } /** Sets the drawing flags of the tile at (x, y) in layer l. */ int tilemap_set_flags(Tilemap * self, int l, int x, int y, int flags) { Tilepane * pane = tilemap_pane(self, l); if (!pane) return -1; return tilepane_set_flags(pane, x, y, flags); } /** Gets the drawing flags of the tile at (x, y) in layer l. */ int tilemap_get_flags(Tilemap * self, int l, int x, int y) { Tilepane * pane = tilemap_pane(self, l); if (!pane) return -1; return tilepane_get_flags(pane, x, y); } /** Draws a tile map. */ void tilemap_draw(Tilemap * map, Camera * camera) { int index; Tilepane * pane; Tilepane * floor = NULL; /* XXX the blends and the shadows don't mix well, perhaps draw shadows * first??? */ for(index = 0; index < TILEMAP_PANES; index++) { pane = tilemap_pane(map, index); if (pane) { tilepane_draw(pane, camera); if (index == 0) { tilepane_draw_blends(pane, camera); floor = pane; } else if (index > 0) { tilepane_draw_shadows_of(pane, floor, camera); } } else { LOG_WARNING("Pane missing: %d", index); } } } /** Draws a layer in the tile map with the given index, without blends nor shadows. */ void tilemap_draw_layer_tiles(Tilemap * map, Camera * camera, int layer) { Tilepane * pane; pane = tilemap_pane(map, layer); if (pane) { tilepane_draw(pane, camera); } } /** Draws the blends in a layer in the tile map with the given index, if it exists. Otherwise does nothing. */ void tilemap_draw_layer_blends(Tilemap * map, Camera * camera, int layer) { Tilepane * pane; pane = tilemap_pane(map, layer); if (pane) { tilepane_draw_blends(pane, camera); } } /** Draws the shadows a given layer in a tile map casts if it exists * and isn't equal to 0. * Otherwise does nothing. */ void tilemap_draw_layer_shadows(Tilemap * map, Camera * camera, int layer) { Tilepane * pane; Tilepane * floor; if (layer < 1) return ; pane = tilemap_pane(map, layer); if (!pane) return; floor = tilemap_pane(map, layer-1); tilepane_draw_shadows_of(pane, floor, camera); } /** Draws a layer in the tile map with the given index, if it exists. Otherwise does nothing. */ void tilemap_draw_layer(Tilemap * map, Camera * camera, int layer) { /* Shadows must be drawn before blends otherwise both won't look good. */ tilemap_draw_layer_tiles( map, camera, layer); tilemap_draw_layer_shadows( map, camera, layer); tilemap_draw_layer_blends( map, camera, layer); } /** Updates the tile map. Currently this animates the tiles. */ void tilemap_update(Tilemap * map, double dt) { tileset_update(map->set, dt); } /** Adds a dynamic thing of the given type to the tile map's area */ Thing * tilemap_addthing(Tilemap * self, int index, int kind, int x, int y, int z, int w, int h) { /** XXX this will be hard to find back from the scripting side, fix this! */ return area_new_thing(self->area, kind, x, y, z, w, h); } /** Sets up the camera so it will stay locked in to the given layer of the tile map */ Lockin * tilepane_lockin(Tilepane * pane, Camera * camera) { float x, y, w, h; if(!pane) return NULL; x = 0.0; y = 0.0; w = tilepane_wide(pane); h = tilepane_high(pane); return camera_newlockin(camera, x, y, w, h); } /** Sets up the camera so it will stay locked in to the extent of the given layer of the tile map. */ Lockin * tilemap_layer_lockin(Tilemap * map, int layer, Camera * camera) { Tilepane * pane; Lockin * result; float x, y, w, h; if (!map) return NULL; pane = tilemap_pane(map, layer); return tilepane_lockin(pane, camera); } /* Returns the tile map's area. */ Area * tilemap_area(Tilemap * self) { if(!self) return NULL; return self->area; } /* Returns a thing from this tile map's area. */ Thing * tilemap_thing(Tilemap * self, int index) { Area * area = tilemap_area(self); return area_thing(area, index); } /* Gets the map's grid size from the largest tile layer */ int tilemap_gridwide(Tilemap * self) { int wide = 0; int index; for (index = 0; index < TILEMAP_PANES; index++) { int aid = tilepane_gridwide(tilemap_pane(self, index)); if (aid > wide) { wide = aid; } } return wide; } /* Gets the map's grid size from the largest tile layer */ int tilemap_gridhigh(Tilemap * self) { int high = 0; int index; for (index = 0; index < TILEMAP_PANES; index++) { int aid = tilepane_gridhigh(tilemap_pane(self, index)); if (aid > high) { high = aid; } } return high; } /** Returns the almount of tile panes in this map. */ int tilemap_panes(Tilemap * self) { (void) self; return TILEMAP_PANES; } /** Sets up automatic blending for this tile map. This is called automatically after loading the Tilemap */ bool tilemap_init_blend(Tilemap * self) { bool res = true; bool aid; int index; for (index = 0; index < TILEMAP_PANES; index++) { aid = tilepane_init_blend(tilemap_pane(self, index), index); res = aid && res; } return res; } /* Returns the tileset used for this tile map. */ Tileset * tilemap_tileset(Tilemap * me) { if (!me) return NULL; return me->set; } /** Returns the firstgid for the tileset of this map, * or negative on error */ /* Returns the tileset used for this tile map. */ int tilemap_firstgid(Tilemap * me) { Tileset * set = tilemap_tileset(me); if (!set) return -1; return tileset_firstgid(set); } #ifdef SEPARATE_TILEMAP_LOADER /* Amount of tilemaps that can be loaded at oce. */ #ifndef ERUTA_TILEMAPS_MAX #define ERUTA_TILEMAPS_MAX 16 #endif /* "Globally" loaded tile maps. Allow up to ERUTA_TILEMAPS_MAX tile maps to be * loaded at the same time. */ static Tilemap * tilemaps[ERUTA_TILEMAPS_MAX]; static int active_tilemap = -1; /* Currently active tile map, if any. Negative means, none active. */ int tilemaps_max(void) { return ERUTA_TILEMAPS_MAX; } int tilemaps_out_of_bounds(int index) { if (index < 0) return TRUE; if (index >tilemaps_max()) return TRUE; return false; } Tilemap * tilemaps_put_raw(int index, Tilemap * map) { if(tilemaps_out_of_bounds(index)) return NULL; return tilemaps[index] = map; } /* Puts map at index, unloading any previously loaded map. */ Tilemap * tilemaps_put(int index, Tilemap * map) { if(tilemaps_out_of_bounds(index)) return NULL; if(tilemaps[index]) tilemap_free(tilemaps[index]); return tilemaps[index] = map; } int tilemaps_init(void) { int index; for(index = 0; index < tilemaps_max(); index ++) { tilemaps_put_raw(index, NULL); } } Tilemap * tilemaps_get(int index) { if(tilemaps_out_of_bounds(index)) return NULL; return tilemaps[index]; } /* Deallocates all loaded tile maps. */ int tilemaps_done(void) { int index; for(index = 0; index < tilemaps_max(); index ++) { tilemaps_put(index, NULL); } } #endif