/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * This code is based on the CRAB engine * * Copyright (c) Arvind Raja Yadav * * Licensed under MIT * */ #include "graphics/screen.h" #include "crab/crab.h" #include "crab/text/TextManager.h" #include "crab/TMX/TMXTileSet.h" namespace Crab { using namespace TMX; void TileSet::load(const Common::Path &path, rapidxml::xml_node *node) { if (nodeValid(node)) { loadNum(_firstGid, "firstgid", node); loadStr(_name, "name", node); loadNum(_tileW, "tilewidth", node); loadNum(_tileH, "tileheight", node); _clip.w = _tileW; _clip.h = _tileH; if (nodeValid("image", node)) { rapidxml::xml_node *imgnode = node->first_node("image"); Common::String filename; loadStr(filename, "source", imgnode); _loc = path.appendComponent(filename); _img.load(_loc); _totalRows = _img.h() / _tileH; _totalCols = _img.w() / _tileW; debugC(kDebugGraphics, "Total rows : %d Total cols: %d gid: %d", _totalRows, _totalCols, _firstGid); } } // Prevent divide by zero errors later if (_totalCols == 0) _totalCols = 1; } void TileSetGroup::reset() { for (auto &i : _tileset) i._img.deleteImage(); _tileset.clear(); } void TileSetGroup::load(const Common::Path &path, rapidxml::xml_node *node) { reset(); for (auto n = node->first_node("tileset"); n != nullptr; n = n->next_sibling("tileset")) { TileSet t; t.load(path, n); _tileset.push_back(t); } } void TileSet::draw(const Vector2i &pos, const TileInfo &tile) { if (tile._gid != 0) { _clip.x = ((tile._gid - _firstGid) % _totalCols) * _tileW; _clip.y = ((tile._gid - _firstGid) / _totalCols) * _tileH; _img.draw(pos.x, pos.y, &_clip, tile._flip); } } void TileSet::preDraw(const Vector2i &pos, const TileInfo &tile, Graphics::ManagedSurface *surf) { if (tile._gid != 0) { _clip.x = ((tile._gid - _firstGid) % _totalCols) * _tileW; _clip.y = ((tile._gid - _firstGid) / _totalCols) * _tileH; _img.draw(pos.x, pos.y, &_clip, tile._flip, surf); } } void TileSetGroup::preDraw(MapLayer &layer, const Vector2i &tileSize, Graphics::ManagedSurface *surf) { if (layer._type == LAYER_IMAGE || layer._type == LAYER_AUTOSHOW) return; _start.x = 0; _start.y = 0; _finish.x = layer._tile.size(); _finish.y = layer._tile[0].size(); _v.x = _start.y * tileSize.x; _v.y = _start.x * tileSize.y; for (int x = _start.x; x < _finish.x; ++x) { for (int y = _start.y; y < _finish.y; ++y) { for (int i = _tileset.size() - 1; i >= 0; --i) if (layer._tile[x][y]._gid >= _tileset[i]._firstGid) { _tileset[i].preDraw(_v, layer._tile[x][y], surf); layer._boundRect.push_back(Rect(_v.x, _v.y, tileSize.x, tileSize.y)); break; } _v.x += tileSize.x; } _v.x = _start.y * tileSize.x; _v.y += tileSize.y; } Common::List::iterator rOuter, rInner; // Process the bound rect list to find any rects to merge for (rOuter = layer._boundRect.begin(); rOuter != layer._boundRect.end(); ++rOuter) { rInner = rOuter; while (++rInner != layer._boundRect.end()) { if ((*rOuter).collide(*rInner)) { rOuter->extend(*rInner); layer._boundRect.erase(rInner); rInner = rOuter; } } } } void TileSetGroup::forceDraw(MapLayer &layer, const Rect &camera, const Vector2i &tileSize, const Rect &playerPos) { if (layer._type == LAYER_IMAGE) return; layer._collide = layer._pos.collide(playerPos); // Normal and prop layers are drawn this way // The row and column we start drawing at _start.x = playerPos.y / tileSize.y; _start.y = playerPos.x / tileSize.x; if (_start.x < 0 || _start.y < 0) return; // The row and column we end drawing at _finish.x = (playerPos.y + playerPos.h) / tileSize.y + 1; _finish.y = (playerPos.x + playerPos.w) / tileSize.x + 1; if (layer._type == LAYER_AUTOSHOW) { if (layer._collide) return; _start.x = camera.y / tileSize.y; _start.y = camera.x / tileSize.x; //The row and column we end drawing at _finish.x = (camera.y + camera.h) / tileSize.y + 1; _finish.y = (camera.x + camera.w) / tileSize.x + 1; } if (_finish.x > (int)layer._tile.size()) _finish.x = layer._tile.size(); if (_finish.y > (int)layer._tile[0].size()) _finish.y = layer._tile[0].size(); _v.x = _start.y * tileSize.x - camera.x; _v.y = _start.x * tileSize.y - camera.y; for (int x = _start.x; x < _finish.x; ++x) { for (int y = _start.y; y < _finish.y; ++y) { for (int i = _tileset.size() - 1; i >= 0; --i) if (layer._tile[x][y]._gid >= _tileset[i]._firstGid) { _tileset[i].draw(_v, layer._tile[x][y]); break; } _v.x += tileSize.x; } _v.x = _start.y * tileSize.x - camera.x; _v.y += tileSize.y; } } void TileSetGroup::draw(MapLayer &layer, const Rect &camera, const Vector2i &tileSize, const Rect &playerPos, pyrodactyl::image::Image &img) { if (layer._type == LAYER_IMAGE) layer._img.draw(-1.0f * camera.x * layer._rate.x, -1.0f * camera.y * layer._rate.y); else if (layer._type == LAYER_PARALLAX) { // The row and column we start drawing at _start.x = 0; _start.y = 0; // The row and column we end drawing at _finish.x = layer._tile.size() - 1; _finish.y = layer._tile[0].size() - 1; _v.x = (_start.y * tileSize.x - camera.x) * layer._rate.x; _v.y = (_start.x * tileSize.y - camera.y) * layer._rate.y; for (int x = _start.x; x < _finish.x; ++x) { for (int y = _start.y; y < _finish.y; ++y) { for (int i = _tileset.size() - 1; i >= 0; --i) if (layer._tile[x][y]._gid >= _tileset[i]._firstGid) { _tileset[i].draw(_v, layer._tile[x][y]); break; } _v.x += tileSize.x; } _v.x = (_start.y * tileSize.x - camera.x) * layer._rate.x; _v.y += tileSize.y; } } else { layer._collide = layer._pos.collide(playerPos); // If player is inside the layer bounds, draw normally - else skip drawing if (layer._type == LAYER_AUTOHIDE && !layer._collide) return; // If the player is outside the layer bounds, draw normally - else skip drawing if (layer._type == LAYER_AUTOSHOW && layer._collide) return; // Normal and prop layers are drawn this way // The row and column we start drawing at _start.x = camera.y / tileSize.y; _start.y = camera.x / tileSize.x; // The row and column we end drawing at _finish.x = (camera.y + camera.h) / tileSize.y + 1; _finish.y = (camera.x + camera.w) / tileSize.x + 1; if (_finish.x > (int)layer._tile.size()) _finish.x = layer._tile.size(); if (_finish.y > (int)layer._tile[0].size()) _finish.y = layer._tile[0].size(); _v.x = camera.x; _v.y = camera.y; Vector2i end; end.x = camera.x + g_engine->_screen->w; end.y = camera.y + g_engine->_screen->h; Rect clip(_v.x, _v.y, end.x - _v.x, end.y - _v.y); img.fastDraw(0, 0, &clip); } } } // End of namespace Crab