#include "IsoObjectsMap.h" using namespace graphic; using namespace std; // constructor IsoObjectsMap::IsoObjectsMap(IsoTilesMap * tm) { // store the tiles map _tm = tm; // no objects at the creation _num_objects = 0; for(int i = 0; i < _tm->GetNumCells(); i++) _objects_map.push_back(NULL); // number of rows of the map _num_rows = _tm->GetNumRows(); // number of cols of the map _num_cols = _tm->GetNumCols(); // dimensions of the map _width = _tm->GetW(); _height = _tm->GetH(); } // add an object to the Map at the [row, col] position void IsoObjectsMap::AddObject(IsoObject * obj, int row, int col) { // increment number of objects _num_objects++; // set object indexes on the tiles map obj->SetIndexes(row, col); // set object absolute position respect the tiles map obj->SetPosition(_tm->GetTileX(row, col), _tm->GetTileY(row, col) + ((_tm->GetTileH() / 2) * (obj->GetNumCols() + 1)) - obj->GetH()); // store the object _objects.push_back(obj); // update the tiles map so that tiles under the object are not walkable for(int r = row - (obj->GetNumRows() - 1) ; r <= row; r++) { for(int c = col + (obj->GetNumCols() - 1) ; c >= col; c--) { _objects_map[(r * _num_cols) + c] = obj; _tm->SetWalkable(r, c, false); } } SortObjects(); } // remove an object from the map, this method does NOT delete the object void IsoObjectsMap::RemoveObject(IsoObject * obj) { vector < IsoObject * >::iterator obj_to_del =_objects.begin(); vector < IsoObject * >::iterator cur_to_del =_cur_objects.begin(); bool found = false; bool found_cur = false; for(int i = 0; i < _num_objects; i++) { if((*obj_to_del) == obj) { found = true; break; } obj_to_del++; } // remove obj from _objects vector if(found) { _objects.erase(obj_to_del); _num_objects--; // update the tiles map so that tiles under the object are walkable for(int r = obj->GetRow() - (obj->GetNumRows() - 1) ; r <= obj->GetRow() ; r++) { for(int c = obj->GetCol() + (obj->GetNumCols() - 1) ; c >= obj->GetCol(); c--) { _objects_map[(r * _num_cols) + c] = NULL; _tm->SetWalkable(r, c); } } // sort objects after the map has been updated SortObjects(); // look for obj in _cur_objects vector int num_cur_obj = _cur_objects.size(); for (int i = 0; i < num_cur_obj; i++) { if((*cur_to_del) == obj) { found_cur = true; break; } cur_to_del++; } // remove obj from _cur_objects vector if(found_cur) _cur_objects.erase(cur_to_del); } } // blit part of the layer on a surface, if the surface is NULL blit on screen // // NOTE: at the moment BlitLayer checks all the objects to find what // are visible and insert them into _cur_objects vector every time a visible // object has found. // An improvement could be to separate visibility check and blit in order to // blit just objects that are in _cur_objects, but this requires a method to // update _cur_objects vector that has to be called every time something // changes in the scene (scrolling or objects changes). // I'll have some test when there will be a lot of objs on the map. void IsoObjectsMap::BlitLayer(SDL_Rect limits_rect, Surface * dst_surf ) { Sint16 l_limit = limits_rect.x ; Sint16 r_limit = limits_rect.x + limits_rect.w; Sint16 u_limit = limits_rect.y; Sint16 b_limit = limits_rect.y + limits_rect.h; Image * cur_img; IsoObject * cur_obj; bool blit = false; IntPoint p; // remove all elements from the vector _cur_objects.clear(); for (int i = 0; i < _num_objects; i++) { cur_obj = _objects[i]; cur_img = cur_obj->GetImage(); p.x = cur_obj->GetX(); p.y = cur_obj->GetY(); // check if the object is into limits if( (p.x + cur_img->GetW() >= l_limit) && (p.x <= r_limit) && (p.y + cur_img->GetH() >= u_limit) && (p.y <= b_limit) ) { // check if one tile of the object is visible for(int r = cur_obj->GetRow() - (cur_obj->GetNumRows() - 1) ; r <= cur_obj->GetRow(); r++) { for(int c = cur_obj->GetCol() + (cur_obj->GetNumCols() - 1) ; c >= cur_obj->GetCol(); c--) { if(_tm->IsVisible(r, c) == VISIBLE) { blit = true; break; } } if(blit) break; } // blit the object if(blit) { // set blit position of the image cur_img->SetPosition(p.x - l_limit, p.y - u_limit); blitter->Blit(cur_img, dst_surf); // reset blit flag blit = false; // store the object in the current vector _cur_objects.push_back(cur_obj); } } } } // select the object at the x,y position IsoObject * IsoObjectsMap::SelectObject(Sint16 x, Sint16 y) { // reset the current object selected IsoObject * obj_selected = NULL; IsoObject * cur_obj; Image * cur_img; SDL_Rect img_rect; IntPoint p;; p.x = x; p.y = y; int num_obj = _cur_objects.size(); // 1a- test collision in all imgs rect if true go to 2 // 1b- test pixel trans for(int i = 0; i < num_obj; i++) { cur_obj = _cur_objects[i]; cur_img = cur_obj->GetImage(); img_rect.x = cur_obj->GetX(); img_rect.y = cur_obj->GetY(); img_rect.w = cur_img->GetW(); img_rect.h = cur_img->GetH(); // inside the image rect if(p.x >= img_rect.x && p.x <= (img_rect.x + img_rect.w) && p.y >= img_rect.y && p.y <= (img_rect.y + img_rect.h)) { // inside the image if(!cur_img->IsTrans(x - img_rect.x, y - img_rect.y)) obj_selected = cur_obj; } } // 2- test clicked cell if(obj_selected == NULL) { CellInd * ind = _tm->GetCell(x,y); if(ind != NULL) obj_selected = _objects_map[(ind->row * _num_cols) + ind->col]; } // result if(obj_selected != NULL) { // check if one tile of the object is visible for(int r = obj_selected->GetRow() - (obj_selected->GetNumRows() - 1) ; r <= obj_selected->GetRow(); r++) { for(int c = obj_selected->GetCol() + (obj_selected->GetNumCols() - 1) ; c >= obj_selected->GetCol(); c--) { if(_tm->IsVisible(r, c) == VISIBLE) return obj_selected; } } } // no object found return NULL; } // sort objects so to blit front objects after rear objects void IsoObjectsMap::SortObjects() { IsoObject * temp; bool sorted = false; int j , i = 0; while((i < _num_objects) && (!sorted)) { sorted = true; for(j = 0; j < _num_objects - i - 1; j++) { if(*_objects[j] > *_objects[j + 1]) { temp = _objects[j + 1]; _objects[j + 1] = _objects[j]; _objects[j] = temp; sorted = false; } } i++; } } // move an object at the [row, col] position void IsoObjectsMap::MoveObject(IsoObject * obj, int row, int col) { // remove current object occupation for(int r = obj->GetRow() - (obj->GetNumRows() - 1) ; r <= obj->GetRow(); r++) { for(int c = obj->GetCol() + (obj->GetNumCols() - 1) ; c >= obj->GetCol(); c--) { _objects_map[(r * _num_cols) + c] = NULL; _tm->SetWalkable(r, c); } } // set object indexes on the tiles map obj->SetIndexes(row, col); // remove current object occupation for(int r = obj->GetRow() - (obj->GetNumRows() - 1) ; r <= obj->GetRow(); r++) { for(int c = obj->GetCol() + (obj->GetNumCols() - 1) ; c >= obj->GetCol(); c--) { _objects_map[(r * _num_cols) + c] = obj; _tm->SetWalkable(r, c, false); } } // re-sort objects SortObjects(); } // set the visibility range of an object in accord with the view direction and its // visibility radius. void IsoObjectsMap::ComputeObjVisibility(IsoObject * obj, int radius, int direction) { // row and col int r, c; // number of rows and cols occupied by the object int num_r, num_c; // for limits int r_start, r_end; int c_start, c_end; // partially visible radius int pv_radius; r = obj->GetRow(); c = obj->GetCol(); num_r = obj->GetNumRows(); num_c = obj->GetNumCols(); // errors check if(radius < 1 || r < 0 || c < 0) return ; // compute partially visible radius pv_radius = radius / PV_COEFFICIENT; if(pv_radius == 0) pv_radius = 1; // N || S || W || E if((direction % 2) == 0) { // COMPUTE PARTIALLY VISIBILITY RANGE switch(direction) { case NORTH: r_start = r - radius - num_r + 1 - pv_radius ; r_end = r + 1; c_start = c - radius - pv_radius; c_end = c + radius + num_c - 1 + pv_radius; break; case SOUTH: r_start = r - 1; r_end = r + radius + pv_radius; c_start = c - radius - pv_radius; c_end = c + radius + num_c - 1 + pv_radius; break; case EAST: r_start = r - radius - num_r + 1 - pv_radius; r_end = r + radius + pv_radius; c_start = c - 1; c_end = c + radius + num_c - 1 + pv_radius; break; case WEST: r_start = r - radius - num_r + 1 - pv_radius; r_end = r + radius + pv_radius; c_start = c - radius - pv_radius; c_end = c + num_c; break; case ANY: r_start = r - radius - num_r - 1; r_end = r + radius; c_start = c - radius; c_end = c + radius + num_c - 1; break; default: return ; break; } // boundaries checks if(r_start < 0) r_start = 0; if(c_start < 0) c_start = 0; if(c_start >= _num_cols) c_start = _num_cols - 1; if(r_end >= _num_rows) r_end = _num_rows - 1; if(c_end >= _num_cols) c_end = _num_cols - 1; // set PARTIALLY VISIBLE range for(int row = r_start; row <= r_end; row++) for(int col = c_start; col <= c_end ; col++) if(_tm->IsVisible(row, col) != VISIBLE) _tm->SetVisibility(row, col, PARTIALLY_VISIBLE); // COMPUTE PARTIALLY VISIBILITY RANGE switch(direction) { case NORTH: r_start = r - radius - num_r + 1; r_end = r; c_start = c - radius; c_end = c + radius + num_c - 1; break; case SOUTH: r_start = r; r_end = r + radius; c_start = c - radius; c_end = c + radius + num_c - 1; break; case EAST: r_start = r - radius - num_r + 1; r_end = r + radius; c_start = c; c_end = c + radius + num_c - 1; break; case WEST: r_start = r - radius - num_r + 1; r_end = r + radius; c_start = c - radius; c_end = c + num_c - 1; break; case ANY: r_start = r - radius - num_r + 1; r_end = r + radius; c_start = c - radius; c_end = c + radius + num_c - 1; break; default: return ; break; } // boundaries checks if(r_start < 0) r_start = 0; if(c_start < 0) c_start = 0; if(c_start >= _num_cols) c_start = _num_cols - 1; if(r_end >= _num_rows) r_end = _num_rows - 1; if(c_end >= _num_cols) c_end = _num_cols - 1; // set VISIBLE range for(int row = r_start ; row <= r_end ; row++) for(int col = c_start ; col <= c_end ; col++) _tm->SetVisibility(row, col); } // NW || NE || SW || SE else { int col_inc = 0; // COMPUTE VISIBILITY RANGE switch(direction) { case NORTH_EAST: r_start = r + radius + pv_radius + 1 - num_r + 1; r_end = r - radius - pv_radius; c_start = c + radius + pv_radius; c_end = c + radius + pv_radius + num_c - 1; // set PARTIALLY VISIBLE range for(int row = r_start; row >= r_end; row--) { for(int col = c_start; col >= c_end - col_inc; col--) if(row >= 0 && row < _num_rows && col >= 0 && col < _num_cols && _tm->IsVisible(row, col) != VISIBLE) _tm->SetVisibility(row, col, PARTIALLY_VISIBLE); col_inc++; } break; case NORTH_WEST: r_start = r + radius + pv_radius + 1 - num_r + 1; r_end = r - radius - pv_radius; c_start = c - radius - pv_radius; c_end = c - radius - pv_radius + num_c - 1; // set PARTIALLY VISIBLE range for(int row = r_start; row >= r_end; row--) { for(int col = c_start; col <= c_end + col_inc; col++) if(row >= 0 && row < _num_rows && col >= 0 && col < _num_cols && _tm->IsVisible(row, col) != VISIBLE) _tm->SetVisibility(row, col, PARTIALLY_VISIBLE); col_inc++; } break; case SOUTH_EAST: r_start = r - radius - pv_radius - 1; r_end = r + radius + pv_radius; c_start = c + radius + pv_radius; c_end = c + radius + pv_radius + num_c - 1; // set PARTIALLY VISIBLE range for(int row = r_start; row <= r_end; row++) { for(int col = c_start; col >= c_end - col_inc; col--) if(row >= 0 && row < _num_rows && col >= 0 && col < _num_cols && _tm->IsVisible(row, col) != VISIBLE) _tm->SetVisibility(row, col, PARTIALLY_VISIBLE); col_inc++; } break; case SOUTH_WEST: r_start = r - radius - pv_radius - 1; r_end = r + radius + pv_radius; c_start = c - radius - pv_radius; c_end = c - radius - pv_radius + num_c - 1; // set PARTIALLY VISIBLE range for(int row = r_start; row <= r_end; row++) { for(int col = c_start; col <= c_end + col_inc; col++) if(row >= 0 && row < _num_rows && col >= 0 && col < _num_cols && _tm->IsVisible(row, col) != VISIBLE) _tm->SetVisibility(row, col, PARTIALLY_VISIBLE); col_inc++; } break; default: return ; break; } col_inc = 0; // COMPUTE VISIBILITY RANGE switch(direction) { case NORTH_EAST: r_start = r + radius - num_r + 1; r_end = r - radius ; c_start = c + radius; c_end = c + radius + num_c - 1; // set PARTIALLY VISIBLE range for(int row = r_start; row >= r_end; row--) { for(int col = c_start; col >= c_end - col_inc; col--) if(row >= 0 && row < _num_rows && col >= 0 && col < _num_cols) _tm->SetVisibility(row, col); col_inc++; } break; case NORTH_WEST: r_start = r + radius - num_r + 1; r_end = r - radius; c_start = c - radius; c_end = c - radius + num_c - 1; // set PARTIALLY VISIBLE range for(int row = r_start; row >= r_end; row--) { for(int col = c_start; col <= c_end + col_inc; col++) if(row >= 0 && row < _num_rows && col >= 0 && col < _num_cols) _tm->SetVisibility(row, col); col_inc++; } break; case SOUTH_EAST: r_start = r - radius; r_end = r + radius; c_start = c + radius; c_end = c + radius + num_c - 1; // set PARTIALLY VISIBLE range for(int row = r_start; row <= r_end; row++) { for(int col = c_start; col >= c_end - col_inc; col--) if(row >= 0 && row < _num_rows && col >= 0 && col < _num_cols) _tm->SetVisibility(row, col); col_inc++; } break; case SOUTH_WEST: r_start = r - radius; r_end = r + radius ; c_start = c - radius; c_end = c - radius + num_c - 1; // set PARTIALLY VISIBLE range for(int row = r_start; row <= r_end; row++) { for(int col = c_start; col <= c_end + col_inc; col++) if(row >= 0 && row < _num_rows && col >= 0 && col < _num_cols) _tm->SetVisibility(row, col); col_inc++; } break; default: return ; break; } } } // vertical flip of the layer void IsoObjectsMap::VFlip() { IsoObject * obj; // reset maps for(int r = 0; r < _tm->GetNumRows(); r++) { for(int c = 0; c < _tm->GetNumCols(); c++) { _objects_map[(r * _tm->GetNumCols()) + c] = NULL; _tm->SetWalkable(r, c); } } // flip all objects for (int i = 0; i < _num_objects; i++) { obj = _objects[i]; // flip the element ((Element *)obj)->Flip(); // set new position obj->SetIndexes(_tm->GetNumRows() - obj->GetRow() + obj->GetNumRows() - 2, _tm->GetNumCols() - obj->GetCol() - obj->GetNumCols()); // set object absolute position respect the tiles map obj->SetPosition(_tm->GetTileX(obj->GetRow(), obj->GetCol()), _tm->GetTileY(obj->GetRow(), obj->GetCol()) + ((_tm->GetTileH() / 2) * (obj->GetNumCols() + 1)) - obj->GetH()); // remove current object occupation for(int r = obj->GetRow() - (obj->GetNumRows() - 1) ; r <= obj->GetRow(); r++) { for(int c = obj->GetCol() + (obj->GetNumCols() - 1) ; c >= obj->GetCol(); c--) { _objects_map[(r * _num_cols) + c] = obj; _tm->SetWalkable(r, c, false); } } } // re-sort objects SortObjects(); }