Files
GDevelop/Extensions/TileMapObject/TileSet.cpp
T
2014-09-24 16:25:13 +02:00

342 lines
10 KiB
C++

/**
GDevelop - Tile Map Extension
Copyright (c) 2014 Victor Levasseur (victorlevasseur52@gmail.com)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include "TileSet.h"
#include <algorithm>
#include <iostream>
#ifdef GD_IDE_ONLY
#include <wx/file.h>
#include <wx/filefn.h>
#include <wx/filename.h>
#endif
#include <GDCore/CommonTools.h>
#ifdef GD_IDE_ONLY
wxBitmap TileSet::m_invalidBitmap = wxBitmap();
#endif
TileHitbox TileHitbox::Rectangle(sf::Vector2f tileSize)
{
TileHitbox hitbox;
hitbox.collidable = true;
hitbox.hitbox = Polygon2d::CreateRectangle(tileSize.x, tileSize.y);
hitbox.hitbox.Move(tileSize.x/2.f, tileSize.y/2.f);
return hitbox;
}
TileHitbox TileHitbox::Triangle(sf::Vector2f tileSize, TriangleOrientation orientation)
{
TileHitbox hitbox;
hitbox.collidable = true;
if(orientation != TileHitbox::BottomRight)
hitbox.hitbox.vertices.push_back(sf::Vector2f(0,0));
if(orientation != TileHitbox::BottomLeft)
hitbox.hitbox.vertices.push_back(sf::Vector2f(tileSize.x,0));
if(orientation != TileHitbox::TopLeft)
hitbox.hitbox.vertices.push_back(tileSize);
if(orientation != TileHitbox::TopRight)
hitbox.hitbox.vertices.push_back(sf::Vector2f(0,tileSize.y));
return hitbox;
}
void TileHitbox::SerializeTo(gd::SerializerElement &element) const
{
element.SetAttribute("collidable", collidable);
//Serialize the polygon
std::string polygonStr;
for(std::vector<sf::Vector2f>::const_iterator vertexIt = hitbox.vertices.begin(); vertexIt != hitbox.vertices.end(); vertexIt++)
{
if(vertexIt != hitbox.vertices.begin())
polygonStr += "|";
polygonStr += gd::ToString(vertexIt->x) + ";" + gd::ToString(vertexIt->y);
}
element.SetAttribute("polygon", polygonStr);
}
void TileHitbox::UnserializeFrom(const gd::SerializerElement &element, sf::Vector2f defaultTileSize)
{
collidable = element.GetBoolAttribute("collidable", true);
hitbox.vertices.clear();
std::string defaultPolygonStr = "0;0|"
+ gd::ToString(defaultTileSize.x) + ";0|"
+ gd::ToString(defaultTileSize.x) + ";" + gd::ToString(defaultTileSize.y) + "|"
+ "0;" + gd::ToString(defaultTileSize.y);
std::string polygonStr = element.GetStringAttribute("polygon", defaultPolygonStr);
std::vector<std::string> vertices = gd::SplitString<std::string>(polygonStr, '|');
for(std::vector<std::string>::iterator vertexIt = vertices.begin(); vertexIt != vertices.end(); vertexIt++)
{
hitbox.vertices.push_back(sf::Vector2f(gd::ToFloat(gd::SplitString<std::string>(*vertexIt, ';')[0]),
gd::ToFloat(gd::SplitString<std::string>(*vertexIt, ';')[1])
));
}
}
TileSet::TileSet() : textureName(), tileSize(24, 24), tileSpacing(0, 0), m_tilesetTexture(), m_dirty(true)
{
}
TileSet::~TileSet()
{
}
void TileSet::LoadResources(RuntimeGame &game)
{
m_dirty = true;
m_tilesetTexture = game.GetImageManager()->GetSFMLTexture(textureName);
}
void TileSet::LoadResources(gd::Project &game)
{
m_dirty = true;
if(game.GetResourcesManager().HasResource(textureName))
{
gd::ImageResource & image = dynamic_cast<gd::ImageResource&>(game.GetResourcesManager().GetResource(textureName));
//Load the resource into a wxBitmap (IDE only) and also get its SFMLTextureWrapper
#ifdef GD_IDE_ONLY
//Force to change the working directory to make it work
wxString oldWorkingDir = wxGetCwd();
std::cout << "Old WD : " << oldWorkingDir << std::endl;
wxSetWorkingDirectory(wxFileName::FileName(game.GetProjectFile()).GetPath());
#endif
m_tilesetTexture = game.GetImageManager()->GetSFMLTexture(textureName);
#ifdef GD_IDE_ONLY
wxSetWorkingDirectory(oldWorkingDir);
if ( wxFileExists(image.GetAbsoluteFile(game)) )
{
std::cout << "Loading for tileset : " << image.GetAbsoluteFile(game) << std::endl;
wxBitmap bmp( image.GetAbsoluteFile(game), wxBITMAP_TYPE_ANY);
m_tilesetBitmap = bmp;
}
#endif
}
else
{
m_tilesetTexture = boost::shared_ptr<SFMLTextureWrapper>();
}
}
void TileSet::Generate()
{
m_dirty = true;
if (!m_tilesetTexture)
return;
std::cout << "Generating texture coords..." << std::endl;
//Calculates the number of rows and columns in the tileset
int columns(0), rows(0);
if (tileSize.x == 0 || tileSize.y == 0)
return;
columns = (m_tilesetTexture->texture.getSize().x + tileSpacing.x) / (tileSize.x + tileSpacing.x);
rows = (m_tilesetTexture->texture.getSize().y + tileSpacing.y) / (tileSize.y + tileSpacing.y);
//Generate the TextureCoords and the sub-bitmaps (only in IDE)
m_coords.clear();
#ifdef GD_IDE_ONLY
m_bitmaps.clear();
#endif
for(int row = 0; row < rows; row++)
{
for(int col = 0; col < columns; col++)
{
//TileTextureCoords
TileTextureCoords tileCoords;
tileCoords.topLeft = sf::Vector2f(col * (tileSize.x + tileSpacing.x),
row * (tileSize.y + tileSpacing.y));
tileCoords.topRight = sf::Vector2f(col * (tileSize.x + tileSpacing.x) + tileSize.x,
row * (tileSize.y + tileSpacing.y));
tileCoords.bottomRight = sf::Vector2f(col * (tileSize.x + tileSpacing.x) + tileSize.x,
row * (tileSize.y + tileSpacing.y) + tileSize.y);
tileCoords.bottomLeft = sf::Vector2f(col * (tileSize.x + tileSpacing.x),
row * (tileSize.y + tileSpacing.y) + tileSize.y);
m_coords.push_back(tileCoords);
#ifdef GD_IDE_ONLY
//sub-wxBitmap
wxBitmap subbitmap;
subbitmap = m_tilesetBitmap.GetSubBitmap(wxRect(col * (tileSize.x + tileSpacing.x),
row * (tileSize.y + tileSpacing.y),
tileSize.x,
tileSize.y));
m_bitmaps.push_back(subbitmap);
#endif
}
}
//Puts the default hitbox for new tiles (if there are more tiles than before)
if (GetTilesCount() > m_hitboxes.size())
m_hitboxes.insert(m_hitboxes.end(), (GetTilesCount()-m_hitboxes.size()), TileHitbox::Rectangle(tileSize));
std::cout << "OK" << std::endl;
m_dirty = false;
}
void TileSet::ResetHitboxes()
{
m_hitboxes.clear();
if (m_dirty)
return;
m_hitboxes.assign(GetTilesCount(), TileHitbox::Rectangle(tileSize));
}
int TileSet::GetTileIDFromPosition(sf::Vector2f position)
{
int columns = GetColumnsCount();
int rows = GetRowsCount();
int tileColumn = position.x / tileSize.x;
int tileRow = position.y / tileSize.y;
return (tileColumn * columns + tileRow);
}
int TileSet::GetTileIDFromCell(int col, int row)
{
int columns = GetColumnsCount();
int rows = GetRowsCount();
return (row * columns + col);
}
#ifdef GD_IDE_ONLY
const wxBitmap& TileSet::GetWxBitmap() const
{
return m_tilesetBitmap;
}
const wxBitmap& TileSet::GetTileBitmap(int id) const
{
return (id < m_bitmaps.size() ? m_bitmaps.at(id) : m_invalidBitmap);
}
#endif
sf::Texture& TileSet::GetTexture()
{
return m_tilesetTexture->texture;
}
const sf::Texture& TileSet::GetTexture() const
{
return m_tilesetTexture->texture;
}
TileTextureCoords TileSet::GetTileTextureCoords(int id) const
{
return m_coords.at(id);
}
sf::Vector2u TileSet::GetSize() const
{
if(!m_tilesetTexture)
return sf::Vector2u(0, 0);
return m_tilesetTexture->texture.getSize();
}
TileHitbox& TileSet::GetTileHitbox(int id)
{
return m_hitboxes[id];
}
const TileHitbox& TileSet::GetTileHitbox(int id) const
{
return m_hitboxes.at(id);
}
int TileSet::GetColumnsCount() const
{
return (m_tilesetTexture->texture.getSize().x + tileSpacing.x) / (tileSize.x + tileSpacing.x);
}
int TileSet::GetRowsCount() const
{
return (m_tilesetTexture->texture.getSize().y + tileSpacing.y) / (tileSize.y + tileSpacing.y);
}
#if defined(GD_IDE_ONLY)
void TileSet::SerializeTo(gd::SerializerElement &element) const
{
element.SetAttribute("textureName", textureName);
element.SetAttribute("tileSizeX", tileSize.x);
element.SetAttribute("tileSizeY", tileSize.y);
element.SetAttribute("tileSpacingX", tileSpacing.x);
element.SetAttribute("tileSpacingY", tileSpacing.y);
gd::SerializerElement &tilesElem = element.AddChild("hitboxes");
//Save polygons
for(std::vector<TileHitbox>::const_iterator it = m_hitboxes.begin(); it != m_hitboxes.end(); it++)
{
gd::SerializerElement &hitboxElem = tilesElem.AddChild("tileHitbox");
it->SerializeTo(hitboxElem);
}
}
#endif
void TileSet::UnserializeFrom(const gd::SerializerElement &element)
{
ResetHitboxes();
textureName = element.GetStringAttribute("textureName", "");
tileSize.x = element.GetIntAttribute("tileSizeX", 32);
tileSize.y = element.GetIntAttribute("tileSizeY", 32);
tileSpacing.x = element.GetIntAttribute("tileSpacingX", 0);
tileSpacing.y = element.GetIntAttribute("tileSpacingY", 0);
if (element.HasChild("hitboxes"))
{
gd::SerializerElement &tilesElem = element.GetChild("hitboxes");
tilesElem.ConsiderAsArrayOf("tileHitbox");
for(int i = 0; i < tilesElem.GetChildrenCount("tileHitbox"); i++)
{
TileHitbox newHitbox;
newHitbox.UnserializeFrom(tilesElem.GetChild(i), tileSize);
m_hitboxes.push_back(newHitbox);
}
}
m_dirty = true;
}