diff options
-rw-r--r-- | tools/tmxcopy/Makefile | 15 | ||||
-rw-r--r-- | tools/tmxcopy/map.cpp | 159 | ||||
-rw-r--r-- | tools/tmxcopy/map.hpp | 37 | ||||
-rw-r--r-- | tools/tmxcopy/readme.txt | 37 | ||||
-rw-r--r-- | tools/tmxcopy/tmx_random_fill.cpp | 116 | ||||
-rw-r--r-- | tools/tmxcopy/tmxcopy.cpp (renamed from tools/tmxcopy/main.cpp) | 0 |
6 files changed, 331 insertions, 33 deletions
diff --git a/tools/tmxcopy/Makefile b/tools/tmxcopy/Makefile index 402e9e4b..975e1980 100644 --- a/tools/tmxcopy/Makefile +++ b/tools/tmxcopy/Makefile @@ -1,15 +1,18 @@ CC=g++ CFLAGS=-g -ggdb -O0 -Wall -c `pkg-config --cflags libxml-2.0` LDFLAGS=`pkg-config --libs libxml-2.0` -SOURCES=base64.cpp main.cpp map.cpp xmlutils.cpp zlibutils.cpp -OBJECTS=$(SOURCES:.cpp=.o) -EXECUTABLE=tmxcopy +SOURCES_UTILS=base64.cpp map.cpp xmlutils.cpp zlibutils.cpp +OBJECTS_UTILS=$(SOURCES_UTILS:.cpp=.o) +EXECUTABLES=tmxcopy tmx_random_fill -all: $(SOURCES) $(EXECUTABLE) +all: $(SOURCES_UTILS) $(EXECUTABLES) make clean -$(EXECUTABLE): $(OBJECTS) - $(CC) $(LDFLAGS) $(OBJECTS) -o $@ +tmxcopy: tmxcopy.o $(OBJECTS_UTILS) + $(CC) $(LDFLAGS) tmxcopy.o $(OBJECTS_UTILS) -o $@ + +tmx_random_fill: tmx_random_fill.o $(OBJECTS_UTILS) + $(CC) $(LDFLAGS) tmx_random_fill.o $(OBJECTS_UTILS) -o $@ .cpp.o: $(CC) $(CFLAGS) $< -o $@ diff --git a/tools/tmxcopy/map.cpp b/tools/tmxcopy/map.cpp index 75cbecbb..30f16a2d 100644 --- a/tools/tmxcopy/map.cpp +++ b/tools/tmxcopy/map.cpp @@ -20,12 +20,12 @@ #include <cstring> #include <iostream> -#include <map> #include <list> #include <string.h> #include <zlib.h> #include <cassert> +#include <ctime> #include "xmlutils.h" #include "zlibutils.h" @@ -194,6 +194,37 @@ Map::Map(std::string filename): std::cout<<"largest GID:"<<mMaxGid<<std::endl<<std::endl; } +/** + * When copying tiles from another map, add new tilesets to this map, and return the translation table. + */ +std::map<int, int> Map::addAndTranslateTilesets(const Map* srcMap) +{ + std::map<int, int> translation; + translation[-1] = -1; + std::vector<Tileset*>* srcTilesets = const_cast<Map*>(srcMap)->getTilesets(); + + for (std::vector<Tileset*>::size_type a = 0; a < srcTilesets->size(); a++) + { + std::vector<Tileset*>::size_type b; + for (b = 0; b < mTilesets.size(); b++) + { + if (*srcTilesets->at(a) == *mTilesets.at(b)) + { + break; + } + } + if (b == mTilesets.size()) + { + mMaxGid += srcTilesets->at(a)->firstgid; + Tileset* destTileset = new Tileset(*srcTilesets->at(a)); + destTileset->firstgid = mMaxGid;//it is possible to get some holes in the gid index this way but who cares, we got 32bit. + mTilesets.push_back(destTileset); + } + translation[a] = b; + } + return translation; +} + bool Map::overwrite( Map* srcMap, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, @@ -245,29 +276,8 @@ bool Map::overwrite( Map* srcMap, if (!checkPassed) return false; - std::map<int, int> translation; - translation[-1] = -1; - std::vector<Tileset*>* srcTilesets = srcMap->getTilesets(); + std::map<int, int> translation = addAndTranslateTilesets(srcMap); - //add new tilesets and add redundant tilesets to list of redundand tilesets - for (int a = 0; a < srcTilesets->size(); a++) - { - int b; - for (b = 0; b < mTilesets.size(); b++) - { - if (*srcTilesets->at(a) == *mTilesets.at(b)) - { - break; - } - } - if (b == mTilesets.size()) - { - mMaxGid += srcTilesets->at(a)->firstgid; - srcTilesets->at(a)->firstgid = mMaxGid;//it is possible to get some holes in the gid index this way but who cares, we got 32bit. - mTilesets.push_back(srcTilesets->at(a)); - } - translation[a] = b; - } //combining layer information for (int i = 0; i < srcMap->getNumberOfLayers(); i++) @@ -329,6 +339,109 @@ bool Map::overwrite( Map* srcMap, return true; } +bool Map::randomFill(Map* templateMap, const std::string& destLayerName, + int destX, int destY, int destWidth, int destHeight, + const ConfigurationOptions& config) +{ + //plausibility check of coordinates + bool checkPassed = true; + if (destX + destWidth > mWidth) + { + std::cerr<<"Error: Area exceeds right map border of target map!"<<std::endl; + checkPassed = false; + } + if (destY + destHeight > mHeight) + { + std::cerr<<"Error: Area exceeds lower map border of target map!"<<std::endl; + checkPassed = false; + } + if (destWidth < templateMap->getWidth()) + { + std::cerr<<"Error: Template is wider than target area"<<std::endl; + checkPassed = false; + } + if (destWidth < templateMap->getHeight()) + { + std::cerr<<"Error: Template is higher than target area"<<std::endl; + checkPassed = false; + } + if (templateMap->getNumberOfLayers() == 0) + { + std::cerr<<"Error: Template has no layers"<<std::endl; + checkPassed = false; + } + if (!config.createMissingLayers && !getLayer(destLayerName)) + { + std::cerr<<"Error: target map has no layer named \""<<destLayerName<<"\""<<std::endl + <<"(and the command-line \"create layers\" option was not used)"<<std::endl; + checkPassed = false; + } + if (!checkPassed) return false; + + std::map<int, int> translation = addAndTranslateTilesets(templateMap); + + + Layer* destLayer = getLayer(destLayerName); + if (!destLayer) + { + destLayer = new Layer(destLayerName, mWidth * mHeight); + mLayers.push_back(destLayer); + std::cout<<"Created new layer "<<destLayerName<<std::endl; + } + + /* Now generate extra tiles. + * TODO Need to configure this for desired density. For 2x1 trees, dW*dH/10 is very sparse, dW*dH/2 is dense */ + srand(time(NULL)); + for (int i = destWidth*destHeight / 10; i > 0; i--) + { + /* Pick a random location, with enough tiles left and down from it to + * fit the template in (the +1 is because it starts on tile (x,y)) + */ + int x = destX + (rand() % (destWidth - templateMap->getWidth () + 1)); + int y = destY + (rand() % (destHeight - templateMap->getHeight() + 1)); + + bool areaIsClear = true; + + for (int loop_y=0; loop_y<templateMap->getHeight(); loop_y++) + { + for (int loop_x=0; loop_x<templateMap->getWidth(); loop_x++) + { + if (! destLayer->getTile(x+loop_x, y+loop_y, mWidth).empty()) + { + areaIsClear = false; + } + } + } + + if (areaIsClear) + { + int p = rand() % templateMap->getNumberOfLayers(); + std::cout <<"Copying pattern "<<p<<" to "<<x<<", "<<y<<std::endl; + + Layer* srcLayer = templateMap->getLayer(p); + for (int loop_y=0; loop_y<templateMap->getHeight(); loop_y++) + { + for (int loop_x=0; loop_x<templateMap->getWidth(); loop_x++) + { + Tile& srcTile = srcLayer->getTile(loop_x, loop_y, templateMap->getWidth()); + Tile& destTile = destLayer->getTile(x+loop_x, y+loop_y, mWidth); + destTile.tileset = translation[srcTile.tileset]; + destTile.index = srcTile.index; + } + } + } + else + { + std::cout <<"Area occupied "<<x<<", "<<y<<std::endl; + } + } + + std::clog<<"copying successful!"<<std::endl; + return true; +} + + + int Map::save(std::string filename) { //remove old tileset and layer information in XML tree diff --git a/tools/tmxcopy/map.hpp b/tools/tmxcopy/map.hpp index af2f5385..4b0ab59a 100644 --- a/tools/tmxcopy/map.hpp +++ b/tools/tmxcopy/map.hpp @@ -21,6 +21,7 @@ #include <string> #include <vector> #include <set> +#include <map> #include <libxml/parser.h> struct ConfigurationOptions @@ -54,12 +55,29 @@ struct Tileset tileheight == a.tileheight ); } + + Tileset() + { + } + + Tileset(const Tileset& src) : + imagefile(src.imagefile), firstgid(src.firstgid), + name(src.name), + tilewidth(src.tilewidth), tileheight(src.tileheight) + { + } + }; struct Tile { int tileset; // number of tileset size_t index; // index in said tileset + + bool empty() + { + return (tileset == -1); + } }; typedef std::vector<Tile> LayerTiles; @@ -67,7 +85,7 @@ typedef std::vector<Tile> LayerTiles; /* This represents an empty tile in the layer. * Note that {0,0} would be the first tile in the first tileset. */ -const Tile defaultTile = {-1, 0}; +const Tile emptyTile = {-1, 0}; class Layer { @@ -76,13 +94,14 @@ class Layer * tileCount - total number of tiles (width*height) */ Layer(std::string name, LayerTiles::size_type tileCount) - : mTiles(tileCount, defaultTile), + : mTiles(tileCount, emptyTile), mName (name) { } std::string getName() { return mName; } Tile& at(LayerTiles::size_type c) { return mTiles.at(c); } + Tile& getTile(int x, int y, int mapWidth) { return mTiles.at(x + y*mapWidth); } private: LayerTiles mTiles; @@ -95,11 +114,23 @@ class Map Map(std::string filename); ~Map(); + /** + * Copies an area from srcMap, replacing its current contents. + */ bool overwrite(Map* srcMap, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, const ConfigurationOptions& config); + /** + * Fills an area of this map with random parts of the template. + * Currently, each layer of the template is treated as an object that + * should be copied in its entirity. + */ + bool randomFill(Map* templateMap, const std::string& destLayerName, + int destX, int destY, int destWidth, int destHeight, + const ConfigurationOptions& config); + int save(std::string filename); int getNumberOfLayers() { return mLayers.size(); } @@ -113,6 +144,8 @@ class Map int getHeight() { return mHeight; } private: + std::map<int, int> addAndTranslateTilesets(const Map* srcMap); + std::vector<Layer*> mLayers; int mWidth; diff --git a/tools/tmxcopy/readme.txt b/tools/tmxcopy/readme.txt index e8ec830a..972d1f66 100644 --- a/tools/tmxcopy/readme.txt +++ b/tools/tmxcopy/readme.txt @@ -1,7 +1,8 @@ -Tmxcopy is a little tool that allows to copy parts of one TMX map to another map. This will make it much easier to match the border areas of maps. The program is command line based. The usage is: +=== TMXCopy === -tmxcopy sourceFile x y height width targetFile x y [outputFile] +Tmxcopy is a little tool that allows to copy parts of one TMX map to another map. This will make it much easier to match the border areas of maps. The program is command line based. The usage is: + tmxcopy [-c] [-n] srcFile x y width height tgtFile x y [outfile] Here an example: When you want to copy the lower right corner (20x20 tiles) of mapA.tmx to the upper left corner of mapB.txt you would open map A with tiled and check at which coordinates the area you want to copy begins. Let's say mapA is 120x130 tiles. Then the area you want to copy would begin at 100:110 and would be 20x20 tiles large. So the first part of the command is: @@ -39,6 +40,38 @@ By default layers are copied to layers of the same name. The -n option will mak The -c option creates layers as needed. Using it to copy mapB to mapA will add a Fencing layer to mapA. +=== TMX Random Fill === + +This is for generating big areas of woodland (or other things that want lots of randomly-placed patterns). + +Usage: tmx_random_fill mapFile destLayer x y width height templateFile [-o outfile] + -c create layers, if they don't already exist in the target + -o save results to outfile, instead of overwriting the original + +Fill a rectangular area of mapFile's layer 'destLayer' with a random selection from the templateFile. + +The template is a map where each layer is a pattern. For example, to make a woodland: + Create a new 2x1 tile map (yes, this is tiny, and only the base of the tree will be visible). + Add the Woodland_x3 tileset, using the correct height (96 pixels). + Make a layer, add a tree. + Make a layer, add the second tree. + Make a layer, add the third tree. + Save this as template_trees.tmx + Run tmx_random_fill with the appropriate options (destLayer will be "Fringe") + +It will then randomly place trees, but only in places where they won't overlap with other things on that layer. The size of the template map is the size of the area which must be empty in the destination layer. + + +=== TMX Translate === + +This tool is still to be written. I guarantee it will be written before I get a woodland map finished. + +A big woodland with lots of randomly-placed trees needs a complex collision layer, most of which can be generated from the visible layers. +This tool takes a two-layer template map - one layer contains visible tiles, and one layer contains collision tiles. + + +=== Bugs (for all these programs) === + The program works so far but there are still some minor problems: -Only tested for TMW-compilant maps. I don't guarantee that it works with Tiled maps that are made for other games and thus use different features. It is assumed that the target map and the source maps have the same number of layers, for example. diff --git a/tools/tmxcopy/tmx_random_fill.cpp b/tools/tmxcopy/tmx_random_fill.cpp new file mode 100644 index 00000000..d55c9b58 --- /dev/null +++ b/tools/tmxcopy/tmx_random_fill.cpp @@ -0,0 +1,116 @@ +/* + * TMXRandomFill + * Copyright (C) 2007 Philipp Sehmisch + * Copyright (C) 2009 Steve Cotton + * + * 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 2 of the License, or + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <iostream> +#include <string> +#include <unistd.h> + +#include "map.hpp" + +void printUsage() +{ + std::cerr<<"Usage: tmx_random_fill [-c] mapFile destLayer x y width height templateFile [-o outfile]"<<std::endl + <<" -c create layers, if they don't already exist in the target"<<std::endl + <<" -o save results to outfile, instead of overwriting the original"<<std::endl + <<std::endl + <<"Fill a rectangular area of mapFile's layer 'destLayer' with a random selection from the templateFile"<<std::endl + <<"TODO - more help here"<<std::endl; +} + +int main(int argc, char * argv[] ) +{ + ConfigurationOptions config = {0}; + std::string outFile; + + int opt; + while ((opt = getopt(argc, argv, "co:")) != -1) + { + switch (opt) + { + case 'c': + config.createMissingLayers = true; + break; + case 'o': + if (optarg) + { + outFile = optarg; + } else { + printUsage(); + return -1; + } + break; + case '?': + std::cerr<<"Unrecognized option"<<std::endl; + printUsage(); + return -1; + } + } + + if ((argc-optind) < 7) + { + std::cerr<<"Too few args"<<std::endl; + printUsage(); + return -1; + } + if ((argc-optind) > 7) + { + std::cerr<<"Too many args"<<std::endl; + printUsage(); + return -1; + } + + std::string mapFile = argv[optind+0]; + std::string destLayer = argv[optind+1]; + int destX= atoi(argv[optind+2]); + int destY= atoi(argv[optind+3]); + int width= atoi(argv[optind+4]); + int height=atoi(argv[optind+5]); + std::string templateFile = argv[optind+6]; + if (outFile.empty()) + { + outFile = mapFile; + } + + // plausibility check of command line options + if (height < 1 || width < 1 || destX < 0 || destY < 0) + { + std::cerr<<"Illegal coordinates!"<<std::endl; + printUsage(); + return -1; + } + + try + { + Map* mainMap = new Map(mapFile); + Map* templateMap = new Map(templateFile); + if (mainMap->randomFill(templateMap, destLayer, destX, destY, width, height, config)) + { + mainMap->save(outFile); + } else { + return -1; + } + delete mainMap; + delete templateMap; + } + catch (int) + { + return -1; + } +} diff --git a/tools/tmxcopy/main.cpp b/tools/tmxcopy/tmxcopy.cpp index 4926d69f..4926d69f 100644 --- a/tools/tmxcopy/main.cpp +++ b/tools/tmxcopy/tmxcopy.cpp |