crimson-0.5.3/0000777000076400001440000000000011152472365010204 500000000000000crimson-0.5.3/tools/0000777000076400001440000000000011152472363011342 500000000000000crimson-0.5.3/tools/mksurface.cpp0000644000076400001440000000660010554366261013750 00000000000000/* Crimson Fields - a game of tactical warfare Copyright (C) 2000-2007 Jens Granseuer 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 (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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* mksurface.cpp */ #include using namespace std; #include "mksurface.h" //////////////////////////////////////////////////////////////////////// // NAME : MkSurface::SaveImageData // DESCRIPTION: Load a bitmap file and save the data into another file. // PARAMETERS : from - name of the bitmap to load // out - file to save to // tp - whether to save color key information // (transparent color). If true we look at the upper // left pixel and assume that should be the color // key (should work for hex images at least). // RETURNS : 0 on success, non-0 on error //////////////////////////////////////////////////////////////////////// int MkSurface::SaveImageData( const string &from, MemBuffer &out, bool tp ) { int rc = LoadBMP( from.c_str() ); if ( rc == 0 ) { if ( s_surface->format->BitsPerPixel == 8 ) { SDL_Palette *palette = s_surface->format->palette; int i; out.Write16( Width() ); out.Write16( Height() ); out.Write8( s_surface->format->BitsPerPixel ); out.Write8( tp ? RAW_DATA_TRANSPARENT : 0 ); out.Write16( palette->ncolors ); // only for <= 256 colors (bpp = 8) if ( tp ) { int ck = GuessColorKey(); // transparent color first out.Write8( palette->colors[ck].r ); out.Write8( palette->colors[ck].g ); out.Write8( palette->colors[ck].b ); } for ( i = 0; i < palette->ncolors; ++i ) { out.Write8( palette->colors[i].r ); out.Write8( palette->colors[i].g ); out.Write8( palette->colors[i].b ); } // save surface lines to file for ( i = 0; i < Height(); ++i ) { Uint8 *pix = (Uint8 *)s_surface->pixels + i * s_surface->pitch; out.Write( pix, Width() ); } } else { cerr << "Error: Couldn't find palette in image " << from << endl << "Make sure it is 8 bit (<= 256 colors) only" << endl; rc = -1; } } else cerr << "Error: Couldn't load image " << from << endl; return rc; } //////////////////////////////////////////////////////////////////////// // NAME : MkSurface::GuessColorKey // DESCRIPTION: Look at the pixel in the upper left corner and assume it // should be transparent (should be true for hex images). // PARAMETERS : - // RETURNS : palette index of the transparent color //////////////////////////////////////////////////////////////////////// int MkSurface::GuessColorKey( void ) const { // only implemented for 8 bit surfaces return *((Uint8 *)s_surface->pixels); } crimson-0.5.3/tools/default.tsrc0000644000076400001440000011725210532350227013602 00000000000000[tileset] name = default icons = CFTiles.bmp class0 = 30 class1 = 81 class2 = 117 class3 = 150 class4 = 250 class5 = 360 class6 = 362 class7 = 361 class8 = 178 class9 = 148 # 000: headquarters [player 1], north [tile] terrain = shop icon = 9 move = 1 color = 138,118,100 # 001: headquarters [player 2], north [tile] terrain = shop icon = 10 move = 1 color = 138,118,100 # 002: headquarters [neutral], north [tile] terrain = shop icon = 8 move = 1 color = 138,118,100 # 003: headquarters [player 1], west [tile] terrain = shop icon = 12 move = 1 color = 138,118,100 # 004: headquarters [player 2], west [tile] terrain = shop icon = 13 move = 1 color = 138,118,100 # 005: headquarters [neutral], west [tile] terrain = shop icon = 11 move = 1 color = 138,118,100 # 006: headquarters [player 1], east [tile] terrain = shop icon = 15 move = 1 color = 138,118,100 # 007: headquarters [player 2], east [tile] terrain = shop icon = 16 move = 1 color = 138,118,100 # 008: headquarters [neutral], east [tile] terrain = shop icon = 14 move = 1 color = 138,118,100 # 009: depot [player 1], north [tile] terrain = shop icon = 18 move = 1 color = 138,118,100 # 010: depot [player 2], north [tile] terrain = shop icon = 19 move = 1 color = 138,118,100 # 011: depot [neutral], north [tile] terrain = shop icon = 17 move = 1 color = 138,118,100 # 012: factory [player 1], north [tile] terrain = shop icon = 36 move = 1 color = 138,118,100 # 013: factory [player 2], north [tile] terrain = shop icon = 37 move = 1 color = 138,118,100 # 014: factory [neutral], north [tile] terrain = shop icon = 35 move = 1 color = 138,118,100 # 015: factory [player 1], east [tile] terrain = shop icon = 39 move = 1 color = 138,118,100 # 016: factory [player 2], east [tile] terrain = shop icon = 40 move = 1 color = 138,118,100 # 017: factory [neutral], east [tile] terrain = shop icon = 38 move = 1 color = 138,118,100 # 018: city [player 1] [tile] terrain = shop icon = 42 move = 1 color = 138,118,100 # 019: city [player 2] [tile] terrain = shop icon = 43 move = 1 color = 138,118,100 # 020: city [neutral] [tile] terrain = shop icon = 41 move = 1 color = 138,118,100 # 021: structure, north [tile] icon = 21 color = 138,118,100 # 022: structure, west [tile] icon = 20 color = 138,118,100 # 023: structure, east [tile] icon = 23 color = 138,118,100 # 024: structure, south [tile] icon = 22 color = 138,118,100 # 025: structure, center [tile] icon = 34 color = 138,118,100 # 026: depot [under construction], north [tile] icon = 44 color = 138,118,100 # 027: depot [under construction], west [tile] icon = 46 color = 138,118,100 # 028: depot [under construction], east [tile] icon = 47 color = 138,118,100 # 029: depot [under construction], south [tile] icon = 45 color = 138,118,100 # 030: plains [tile] terrain = plains icon = 24 move = 2 color = 94,142,52 # 031: plains [slightly rugged] [tile] terrain = plains icon = 25 attack = 1 defend = 1 move = 2 color = 94,142,52 # 032: plains [moderately rugged] [tile] terrain = plains icon = 26 defend = 1 move = 2 color = 94,142,52 # 033: plains [rugged] [tile] terrain = plains icon = 27 attack = 2 defend = 2 move = 2 color = 102,114,29 # 034: plains [few pebbles] [tile] terrain = plains icon = 28 move = 2 color = 94,142,52 # 035: plains [pebbles] [tile] terrain = plains icon = 29 move = 2 color = 94,142,52 # 036: plains [rugged, few pebbles] [tile] terrain = plains icon = 30 attack = 2 defend = 2 move = 2 color = 102,114,29 # 037: plains [rugged, pebbles] [tile] terrain = plains icon = 31 attack = 2 defend = 2 move = 2 color = 102,114,29 # 038: plains [hilly] 1 [tile] terrain = plains icon = 32 attack = 1 move = 2 color = 94,142,52 # 039: plains [hilly] 2 [tile] terrain = plains icon = 1 attack = 1 move = 2 color = 94,142,52 # 040: plains [craters] 1 [tile] terrain = plains icon = 1 attack = 1 move = 2 color = 94,142,52 # 041: plains [craters] 2 [tile] terrain = plains icon = 1 attack = 1 move = 2 color = 94,142,52 # 042: plains [craters] 3 [tile] terrain = plains icon = 1 attack = 1 move = 2 color = 94,142,52 # 043: reserved [tile] icon = 1 color = 0,0,0 # 044: reserved [tile] icon = 1 color = 0,0,0 # 045: reserved [tile] icon = 1 color = 0,0,0 # 046: building [large house], north [tile] terrain = plains icon = 1 defend = 1 move = 2 color = 94,142,52 # 047: building [large house], west [tile] terrain = plains icon = 1 defend = 1 move = 2 color = 94,142,52 # 048: building [large house], east [tile] terrain = plains icon = 1 defend = 1 move = 2 color = 94,142,52 # 049: building [large house], south [tile] terrain = plains icon = 1 defend = 1 move = 2 color = 94,142,52 # 050: building [warehouse], north [tile] icon = 66 defend = 1 move = 2 color = 138,118,100 # 051: building [warehouse], west [tile] icon = 65 defend = 1 move = 2 color = 138,118,100 # 052: building [warehouse], south [tile] icon = 67 defend = 1 move = 2 color = 138,118,100 # 053: building [house] [tile] terrain = plains icon = 61 defend = 1 move = 2 color = 94,142,52 # 054: building [cottage] [tile] terrain = plains icon = 62 defend = 1 move = 2 color = 94,142,52 # 055: building [shed] [tile] terrain = plains icon = 63 defend = 1 move = 2 color = 94,142,52 # 056: building [silos] [tile] terrain = plains icon = 64 defend = 1 move = 2 color = 94,142,52 # 057: building [hangars] [tile] terrain = plains icon = 68 defend = 1 move = 2 color = 104,89,25 # 058: airstrip, north [tile] terrain = plains icon = 69 move = 2 color = 104,89,25 # 059: airstrip, south [tile] terrain = plains icon = 70 move = 2 color = 104,89,25 # 060: landing platform [small] [tile] terrain = road icon = 71 attack = 3 defend = -3 move = 1 color = 50,34,4 # 061: landing platform [large], north [tile] terrain = road icon = 382 attack = 3 defend = -3 move = 1 color = 50,34,4 # 062: landing platform [large], west [tile] terrain = road icon = 380 attack = 3 defend = -3 move = 1 color = 50,34,4 # 063: landing platform [large], east [tile] terrain = road icon = 381 attack = 3 defend = -3 move = 1 color = 50,34,4 # 064: landing platform [large], south [tile] terrain = road icon = 383 attack = 3 defend = -3 move = 1 color = 50,34,4 # 065: hill [small] 1 [tile] terrain = rough icon = 72 attack = 3 defend = 3 move = 3 color = 70,112,31 # 066: hill [small] 2 [tile] terrain = rough icon = 73 attack = 3 defend = 3 move = 3 color = 70,112,31 # 067: hill [small, rugged] [tile] terrain = rough icon = 74 attack = 4 defend = 4 move = 3 color = 102,114,29 # 068: hill [medium], northwest [tile] terrain = rough icon = 75 attack = 3 defend = 3 move = 3 color = 70,112,31 # 069: hill [medium], southeast [tile] terrain = rough icon = 76 attack = 3 defend = 3 move = 3 color = 70,112,31 # 070: hill [large], west [tile] terrain = rough icon = 77 attack = 3 defend = 3 move = 3 color = 70,112,31 # 071: hill [large], north [tile] terrain = rough icon = 79 attack = 3 defend = 3 move = 3 color = 70,112,31 # 072: hill [large], east [tile] terrain = rough icon = 78 attack = 3 defend = 3 move = 3 color = 70,112,31 # 073: hill [large], south [tile] terrain = rough icon = 80 attack = 3 defend = 3 move = 3 color = 70,112,31 # 074: rocks [one] [tile] terrain = plains icon = 97 defend = 1 move = 3 color = 94,142,52 # 075: rocks [two] [tile] terrain = plains icon = 98 defend = 1 move = 3 color = 94,142,52 # 076: rocks [three] [tile] terrain = plains icon = 96 defend = 1 move = 3 color = 94,142,52 # 077: woodlands [very light] [tile] terrain = plains icon = 48 move = 2 color = 94,142,52 # 078: woodlands [light] [tile] terrain = plains icon = 49 attack = 1 defend = 1 move = 2 color = 70,112,31 # 079: woodlands [medium] [tile] terrain = plains icon = 50 attack = 2 defend = 2 move = 2 color = 70,112,31 # 080: woodlands [dense] [tile] terrain = plains icon = 51 attack = 3 defend = 3 move = 2 color = 70,112,31 # 081: forest 1 [tile] terrain = forest icon = 52 attack = 5 defend = 10 move = 3 color = 46,86,20 # 082: forest 2 [tile] terrain = forest icon = 53 attack = 5 defend = 10 move = 3 color = 46,86,20 # 083: forest [large], north [tile] terrain = forest icon = 56 attack = 5 defend = 10 move = 3 color = 46,86,20 # 084: forest [large], west [tile] terrain = forest icon = 54 attack = 5 defend = 10 move = 3 color = 46,86,20 # 085: forest [large], east [tile] terrain = forest icon = 55 attack = 5 defend = 10 move = 3 color = 46,86,20 # 086: forest [large], south [tile] terrain = forest icon = 57 attack = 5 defend = 10 move = 3 color = 46,86,20 # 087: slope [straight], n-s(w) [tile] terrain = rough icon = 86 move = 3 color = 94,142,52 # 088: slope [straight], n-s(e) [tile] terrain = rough icon = 85 move = 3 color = 94,142,52 # 089: slope [straight], nw-se(ne) [tile] terrain = rough icon = 84 move = 3 color = 94,142,52 # 090: slope [straight], nw-se(sw) [tile] terrain = rough icon = 83 move = 3 color = 94,142,52 # 091: slope [straight], sw-ne(nw) [tile] terrain = rough icon = 82 move = 3 color = 94,142,52 # 092: slope [straight], sw-ne(se) [tile] terrain = rough icon = 81 move = 3 color = 94,142,52 # 093: slope [curve], n-se(ne) [tile] terrain = rough icon = 106 move = 3 color = 94,142,52 # 094: slope [curve], n-se(sw) [tile] terrain = rough icon = 91 move = 3 color = 94,142,52 # 095: slope [curve], n-sw(nw) [tile] terrain = rough icon = 95 move = 3 color = 94,142,52 # 096: slope [curve], n-sw(se) [tile] terrain = rough icon = 89 move = 3 color = 94,142,52 # 097: slope [curve], s-ne(nw) [tile] terrain = rough icon = 92 move = 3 color = 94,142,52 # 098: slope [curve], s-ne(se) [tile] terrain = rough icon = 107 move = 3 color = 94,142,52 # 099: slope [curve], s-nw(sw) [tile] terrain = rough icon = 94 move = 3 color = 94,142,52 # 100: slope [curve], s-nw(ne) [tile] terrain = rough icon = 88 move = 3 color = 94,142,52 # 101: slope [curve], nw-ne(n) [tile] terrain = rough icon = 105 move = 3 color = 94,142,52 # 102: slope [curve], nw-ne(s) [tile] terrain = rough icon = 90 move = 3 color = 94,142,52 # 103: slope [curve], sw-se(n) [tile] terrain = rough icon = 87 move = 3 color = 94,142,52 # 104: slope [curve], sw-se(s) [tile] terrain = rough icon = 93 move = 3 color = 94,142,52 # 105: slope [end], n-s(w) [tile] terrain = plains icon = 119 move = 2 color = 94,142,52 # 106: slope [end], n-s(e) [tile] terrain = plains icon = 118 move = 2 color = 94,142,52 # 107: slope [end], s-n(w) [tile] terrain = plains icon = 113 move = 2 color = 94,142,52 # 108: slope [end], s-n(e) [tile] terrain = plains icon = 112 move = 2 color = 94,142,52 # 109: slope [end], nw-se(ne) [tile] terrain = plains icon = 117 move = 2 color = 94,142,52 # 110: slope [end], nw-se(sw) [tile] terrain = plains icon = 116 move = 2 color = 94,142,52 # 111: slope [end], ne-sw(nw) [tile] terrain = plains icon = 115 move = 2 color = 94,142,52 # 112: slope [end], ne-sw(se) [tile] terrain = plains icon = 114 move = 2 color = 94,142,52 # 113: slope [end], sw-ne(nw) [tile] terrain = plains icon = 109 move = 2 color = 94,142,52 # 114: slope [end], sw-ne(se) [tile] terrain = plains icon = 108 move = 2 color = 94,142,52 # 115: slope [end], se-nw(ne) [tile] terrain = plains icon = 111 move = 2 color = 94,142,52 # 116: slope [end], se-nw(sw) [tile] terrain = plains icon = 110 move = 2 color = 94,142,52 # 117: mountain [small] [tile] terrain = mountains icon = 99 attack = 15 defend = 15 move = 4 color = 178,182,180 # 118: mountain [large/huge], north [tile] terrain = mountains icon = 121 attack = 15 defend = 15 move = 4 color = 178,182,180 # 119: mountain [large/huge], south [tile] terrain = mountains icon = 122 attack = 15 defend = 15 move = 4 color = 178,182,180 # 120: mountain [large/huge], west [tile] terrain = mountains icon = 120 attack = 15 defend = 15 move = 4 color = 178,182,180 # 121: mountain [large/huge], east [tile] terrain = mountains icon = 123 attack = 15 defend = 15 move = 4 color = 178,182,180 # 122: mountain [huge], summit [tile] terrain = mountains icon = 126 attack = 15 defend = 15 move = 6 color = 178,182,180 # 123: mountain [huge], northwest [tile] terrain = mountains icon = 124 attack = 15 defend = 15 move = 4 color = 178,182,180 # 124: mountain [huge], northeast [tile] terrain = mountains icon = 127 attack = 15 defend = 15 move = 4 color = 178,182,180 # 125: mountain [huge], southwest [tile] terrain = mountains icon = 125 attack = 15 defend = 15 move = 4 color = 178,182,180 # 126: mountain [huge], southeast [tile] terrain = mountains icon = 128 attack = 15 defend = 15 move = 4 color = 178,182,180 # 127: barricades [little rubble] 1 [tile] terrain = rough icon = 100 attack = 2 defend = 2 move = 3 color = 102,114,29 # 128: barricades [little rubble] 2 [tile] terrain = rough icon = 101 attack = 2 defend = 2 move = 3 color = 102,114,29 # 129: barricades [medium rubble] [tile] terrain = rough icon = 102 attack = 2 defend = 2 move = 3 color = 102,114,29 # 130: barricades [dense rubble] [tile] terrain = rough icon = 103 attack = 2 defend = 2 move = 3 color = 102,114,29 # 131: barricades [crater] [tile] terrain = rough icon = 103 attack = 2 defend = 2 move = 3 color = 102,114,29 # 132: barricades [rugged, tank stoppers] [tile] terrain = rough icon = 104 attack = 4 defend = 4 move = 3 color = 102,114,29 # 133: fence [straight], n-s [tile] terrain = rough icon = 148 attack = 4 defend = 5 move = 3 color = 150,154,132 # 134: fence [straight], nw-se [tile] terrain = rough icon = 150 attack = 4 defend = 5 move = 3 color = 150,154,132 # 135: fence [straight], ne-sw [tile] terrain = rough icon = 149 attack = 4 defend = 5 move = 3 color = 150,154,132 # 136: fence [curve], n-sw [tile] terrain = rough icon = 168 attack = 4 defend = 5 move = 3 color = 150,154,132 # 137: fence [curve], n-se [tile] terrain = rough icon = 169 attack = 4 defend = 5 move = 3 color = 150,154,132 # 138: fence [curve], s-nw [tile] terrain = rough icon = 151 attack = 4 defend = 5 move = 3 color = 150,154,132 # 139: fence [curve], s-ne [tile] terrain = rough icon = 152 attack = 4 defend = 5 move = 3 color = 150,154,132 # 140: fence [curve], nw-ne [tile] terrain = rough icon = 170 attack = 4 defend = 5 move = 3 color = 150,154,132 # 141: fence [curve], sw-se [tile] terrain = rough icon = 171 attack = 4 defend = 5 move = 3 color = 150,154,132 # 142: fence [end], n-s [tile] terrain = rough icon = 173 attack = 4 defend = 5 move = 3 color = 150,154,132 # 143: fence [end], s-n [tile] terrain = rough icon = 172 attack = 4 defend = 5 move = 3 color = 150,154,132 # 144: fence [end], nw-se [tile] terrain = rough icon = 144 attack = 4 defend = 5 move = 3 color = 150,154,132 # 145: fence [end], ne-sw [tile] terrain = rough icon = 147 attack = 4 defend = 5 move = 3 color = 150,154,132 # 146: fence [end], sw-ne [tile] terrain = rough icon = 146 attack = 4 defend = 5 move = 3 color = 150,154,132 # 147: fence [end], se-nw [tile] terrain = rough icon = 145 attack = 4 defend = 5 move = 3 color = 150,154,132 # 148: trenches [straight], n-s [tile] terrain = trenches icon = 129 attack = 5 defend = 10 move = 3 color = 102,114,29 # 149: trenches [straight], nw-se [tile] terrain = trenches icon = 134 attack = 5 defend = 10 move = 3 color = 102,114,29 # 150: trenches [straight], ne-sw [tile] terrain = trenches icon = 133 attack = 5 defend = 10 move = 3 color = 102,114,29 # 151: trenches [curve], nw-ne [tile] terrain = trenches icon = 135 attack = 5 defend = 10 move = 3 color = 102,114,29 # 152: trenches [curve], sw-se [tile] terrain = trenches icon = 136 attack = 5 defend = 10 move = 3 color = 102,114,29 # 153: trenches [curve], n-sw [tile] terrain = trenches icon = 130 attack = 5 defend = 10 move = 3 color = 102,114,29 # 154: trenches [curve], n-se [tile] terrain = trenches icon = 131 attack = 5 defend = 10 move = 3 color = 102,114,29 # 155: trenches [curve], nw-s [tile] terrain = trenches icon = 132 attack = 5 defend = 10 move = 3 color = 102,114,29 # 156: trenches [curve], ne-s [tile] terrain = trenches icon = 153 attack = 5 defend = 10 move = 3 color = 102,114,29 # 157: trenches [junction], nw-se-ne [tile] terrain = trenches icon = 139 attack = 5 defend = 10 move = 3 color = 102,114,29 # 158: trenches [junction], ne-sw-nw [tile] terrain = trenches icon = 138 attack = 5 defend = 10 move = 3 color = 102,114,29 # 159: trenches [junction], sw-ne-se [tile] terrain = trenches icon = 137 attack = 5 defend = 10 move = 3 color = 102,114,29 # 160: trenches [junction], se-nw-sw [tile] terrain = trenches icon = 140 attack = 5 defend = 10 move = 3 color = 102,114,29 # 161: trenches [junction], n-sw-se [tile] terrain = trenches icon = 154 attack = 5 defend = 10 move = 3 color = 102,114,29 # 162: trenches [junction], s-nw-ne [tile] terrain = trenches icon = 155 attack = 5 defend = 10 move = 3 color = 102,114,29 # 163: trenches [junction], n-s-sw [tile] terrain = trenches icon = 156 attack = 5 defend = 10 move = 3 color = 102,114,29 # 164: trenches [junction], n-s-se [tile] terrain = trenches icon = 157 attack = 5 defend = 10 move = 3 color = 102,114,29 # 165: trenches [junction], nw-s-se [tile] terrain = trenches icon = 158 attack = 5 defend = 10 move = 3 color = 102,114,29 # 166: trenches [junction], ne-sw-s [tile] terrain = trenches icon = 159 attack = 5 defend = 10 move = 3 color = 102,114,29 # 167: trenches [junction], n-ne-sw [tile] terrain = trenches icon = 160 attack = 5 defend = 10 move = 3 color = 102,114,29 # 168: trenches [junction], n-nw-se [tile] terrain = trenches icon = 161 attack = 5 defend = 10 move = 3 color = 102,114,29 # 169: trenches [crossing], n-nw-s-se [tile] terrain = trenches icon = 162 attack = 5 defend = 10 move = 3 color = 102,114,29 # 170: trenches [crossing], n-ne-s-sw [tile] terrain = trenches icon = 163 attack = 5 defend = 10 move = 3 color = 102,114,29 # 171: trenches [crossing], nw-ne-sw-se [tile] terrain = trenches icon = 164 attack = 5 defend = 10 move = 3 color = 102,114,29 # 172: trenches [circular] [tile] terrain = trenches icon = 167 attack = 5 defend = 10 move = 3 color = 102,114,29 # 173: trenches [barbed wire], n-s [tile] terrain = trenches icon = 141 attack = 5 defend = 15 move = 3 color = 102,114,29 # 174: trenches [barbed wire], nw-se [tile] terrain = trenches icon = 143 attack = 5 defend = 15 move = 3 color = 102,114,29 # 175: trenches [barbed wire], sw-ne [tile] terrain = trenches icon = 142 attack = 5 defend = 15 move = 3 color = 102,114,29 # 176: road [straight], n-s [tile] terrain = road icon = 193 attack = 3 defend = -3 move = 1 color = 50,34,4 # 177: road [straight], nw-se [tile] terrain = road icon = 192 attack = 3 defend = -3 move = 1 color = 50,34,4 # 178: road [straight], ne-sw [tile] terrain = road icon = 194 attack = 3 defend = -3 move = 1 color = 50,34,4 # 179: road [curve], n-sw [tile] terrain = road icon = 198 attack = 3 defend = -3 move = 1 color = 50,34,4 # 180: road [curve], n-se [tile] terrain = road icon = 197 attack = 3 defend = -3 move = 1 color = 50,34,4 # 181: road [curve], s-nw [tile] terrain = road icon = 199 attack = 3 defend = -3 move = 1 color = 50,34,4 # 182: road [curve], s-ne [tile] terrain = road icon = 200 attack = 3 defend = -3 move = 1 color = 50,34,4 # 183: road [curve], nw-ne [tile] terrain = road icon = 202 attack = 3 defend = -3 move = 1 color = 50,34,4 # 184: road [curve], sw-se [tile] terrain = road icon = 201 attack = 3 defend = -3 move = 1 color = 50,34,4 # 185: road [junction], n-sw-se [tile] terrain = road icon = 225 attack = 3 defend = -3 move = 1 color = 50,34,4 # 186: road [junction], s-nw-ne [tile] terrain = road icon = 226 attack = 3 defend = -3 move = 1 color = 50,34,4 # 187: road [junction], n-s-sw [tile] terrain = road icon = 216 attack = 3 defend = -3 move = 1 color = 50,34,4 # 188: road [junction], n-s-se [tile] terrain = road icon = 206 attack = 3 defend = -3 move = 1 color = 50,34,4 # 189: road [junction], s-n-nw [tile] terrain = road icon = 218 attack = 3 defend = -3 move = 1 color = 50,34,4 # 190: road [junction], s-n-ne [tile] terrain = road icon = 217 attack = 3 defend = -3 move = 1 color = 50,34,4 # 191: road [junction], nw-se-s [tile] terrain = road icon = 205 attack = 3 defend = -3 move = 1 color = 50,34,4 # 192: road [junction], ne-sw-s [tile] terrain = road icon = 204 attack = 3 defend = -3 move = 1 color = 50,34,4 # 193: road [junction], sw-ne-n [tile] terrain = road icon = 195 attack = 3 defend = -3 move = 1 color = 50,34,4 # 194: road [junction], se-nw-n [tile] terrain = road icon = 196 attack = 3 defend = -3 move = 1 color = 50,34,4 # 195: road [junction], nw-se-ne [tile] terrain = road icon = 221 attack = 3 defend = -3 move = 1 color = 50,34,4 # 196: road [junction], ne-sw-nw [tile] terrain = road icon = 222 attack = 3 defend = -3 move = 1 color = 50,34,4 # 197: road [junction], sw-ne-se [tile] terrain = road icon = 223 attack = 3 defend = -3 move = 1 color = 50,34,4 # 198: road [junction], se-nw-sw [tile] terrain = road icon = 224 attack = 3 defend = -3 move = 1 color = 50,34,4 # 199: road [crossing], n-s-nw-se [tile] terrain = road icon = 219 attack = 3 defend = -3 move = 1 color = 50,34,4 # 200: road [crossing], n-s-ne-sw [tile] terrain = road icon = 220 attack = 3 defend = -3 move = 1 color = 50,34,4 # 201: road [crossing], nw-se-ne-sw [tile] terrain = road icon = 203 attack = 3 defend = -3 move = 1 color = 50,34,4 # 202: road [craters], n-s [tile] terrain = road icon = 177 attack = 2 defend = -3 move = 2 color = 50,34,4 # 203: road [craters], nw-se [tile] terrain = road icon = 178 attack = 2 defend = -3 move = 2 color = 50,34,4 # 204: road [craters], ne-sw [tile] terrain = road icon = 179 attack = 2 defend = -3 move = 2 color = 50,34,4 # 205: road [tank stoppers], n-s [tile] terrain = rough icon = 174 attack = 5 defend = -2 move = 2 color = 50,34,4 # 206: road [tank stoppers], nw-se [tile] terrain = rough icon = 175 attack = 5 defend = -2 move = 2 color = 50,34,4 # 207: road [tank stoppers], ne-sw [tile] terrain = rough icon = 176 attack = 5 defend = -2 move = 2 color = 50,34,4 # 208: road [bridge], n-s [tile] terrain = road icon = 183 attack = 3 defend = -3 move = 1 color = 50,34,4 # 209: road [bridge], nw-se [tile] terrain = road icon = 184 attack = 3 defend = -3 move = 1 color = 50,34,4 # 210: road [bridge], ne-sw [tile] terrain = road icon = 185 attack = 3 defend = -3 move = 1 color = 50,34,4 # 211: road [destroyed bridge], n-s [tile] terrain = water icon = 186 move = 2 color = 50,118,148 # 212: road [destroyed bridge], nw-se [tile] terrain = water icon = 187 move = 2 color = 50,118,148 # 213: road [destroyed bridge], ne-sw [tile] terrain = water icon = 188 move = 2 color = 50,118,148 # 214: road [bridge], n-s + coast, nw-n-ne [tile] terrain = road icon = 207 attack = 3 defend = -3 move = 1 color = 50,34,4 # 215: road [bridge], n-s + coast, sw-s-se [tile] terrain = road icon = 208 attack = 3 defend = -3 move = 1 color = 50,34,4 # 216: road [bridge], nw-se + coast, n-nw [tile] terrain = road icon = 231 attack = 3 defend = -3 move = 1 color = 50,34,4 # 217: road [bridge], nw-se + coast, s-se [tile] terrain = road icon = 232 attack = 3 defend = -3 move = 1 color = 50,34,4 # 218: road [bridge], nw-se + coast, n-nw-sw-s [tile] terrain = road icon = 211 attack = 3 defend = -3 move = 1 color = 50,34,4 # 219: road [bridge], nw-se + coast, n-ne-se-s [tile] terrain = road icon = 212 attack = 3 defend = -3 move = 1 color = 50,34,4 # 220: road [bridge], ne-sw + coast, n-nw-sw-s [tile] terrain = road icon = 209 attack = 3 defend = -3 move = 1 color = 50,34,4 # 221: road [bridge], ne-sw + coast, n-ne-se-s [tile] terrain = road icon = 210 attack = 3 defend = -3 move = 1 color = 50,34,4 # 222: path [straight], n-s [tile] terrain = road icon = 240 attack = 3 defend = -3 move = 2 color = 104,89,25 # 223: path [straight], nw-se [tile] terrain = road icon = 241 attack = 3 defend = -3 move = 2 color = 104,89,25 # 224: path [straight], ne-sw [tile] terrain = road icon = 242 attack = 3 defend = -3 move = 2 color = 104,89,25 # 225: path [curve], n-sw [tile] terrain = road icon = 245 attack = 3 defend = -3 move = 2 color = 104,89,25 # 226: path [curve], n-se [tile] terrain = road icon = 246 attack = 3 defend = -3 move = 2 color = 104,89,25 # 227: path [curve], s-ne [tile] terrain = road icon = 247 attack = 3 defend = -3 move = 2 color = 104,89,25 # 228: path [curve], s-nw [tile] terrain = road icon = 248 attack = 3 defend = -3 move = 2 color = 104,89,25 # 229: path [curve], nw-ne [tile] terrain = road icon = 244 attack = 3 defend = -3 move = 2 color = 104,89,25 # 230: path [curve], sw-se [tile] terrain = road icon = 243 attack = 3 defend = -3 move = 2 color = 104,89,25 # 231: path [junction], n-sw-se [tile] terrain = road icon = 273 attack = 3 defend = -3 move = 2 color = 104,89,25 # 232: path [junction], s-nw-ne [tile] terrain = road icon = 274 attack = 3 defend = -3 move = 2 color = 104,89,25 # 233: path [junction], n-s-sw [tile] terrain = road icon = 252 attack = 3 defend = -3 move = 2 color = 104,89,25 # 234: path [junction], n-s-se [tile] terrain = road icon = 253 attack = 3 defend = -3 move = 2 color = 104,89,25 # 235: path [junction], s-n-nw [tile] terrain = road icon = 255 attack = 3 defend = -3 move = 2 color = 104,89,25 # 236: path [junction], s-n-ne [tile] terrain = road icon = 254 attack = 3 defend = -3 move = 2 color = 104,89,25 # 237: path [junction], nw-se-s [tile] terrain = road icon = 228 attack = 3 defend = -3 move = 2 color = 104,89,25 # 238: path [junction], ne-sw-s [tile] terrain = road icon = 229 attack = 3 defend = -3 move = 2 color = 104,89,25 # 239: path [junction], sw-ne-n [tile] terrain = road icon = 256 attack = 3 defend = -3 move = 2 color = 104,89,25 # 240: path [junction], se-nw-n [tile] terrain = road icon = 227 attack = 3 defend = -3 move = 2 color = 104,89,25 # 241: path [junction], nw-se-ne [tile] terrain = road icon = 230 attack = 3 defend = -3 move = 2 color = 104,89,25 # 242: path [junction], ne-sw-nw [tile] terrain = road icon = 270 attack = 3 defend = -3 move = 2 color = 104,89,25 # 243: path [junction], sw-ne-se [tile] terrain = road icon = 271 attack = 3 defend = -3 move = 2 color = 104,89,25 # 244: path [junction], se-nw-sw [tile] terrain = road icon = 272 attack = 3 defend = -3 move = 2 color = 104,89,25 # 245: path [crossing], n-s-nw-se [tile] terrain = road icon = 249 attack = 3 defend = -3 move = 2 color = 104,89,25 # 246: path [crossing], n-s-ne-sw [tile] terrain = road icon = 251 attack = 3 defend = -3 move = 2 color = 104,89,25 # 247: path [crossing], nw-se-ne-sw [tile] terrain = road icon = 250 attack = 3 defend = -3 move = 2 color = 104,89,25 # 248: rails [straight], n-s [tile] terrain = rails icon = 239 attack = 2 defend = -3 move = 2 color = 50,34,4 # 249: rails [straight], nw-se [tile] terrain = rails icon = 257 attack = 2 defend = -3 move = 2 color = 50,34,4 # 250: rails [straight], ne-sw [tile] terrain = rails icon = 258 attack = 2 defend = -3 move = 2 color = 50,34,4 # 251: rails [curve], n-sw [tile] terrain = rails icon = 261 attack = 2 defend = -3 move = 2 color = 50,34,4 # 252: rails [curve], n-se [tile] terrain = rails icon = 262 attack = 2 defend = -3 move = 2 color = 50,34,4 # 253: rails [curve], s-nw [tile] terrain = rails icon = 281 attack = 2 defend = -3 move = 2 color = 50,34,4 # 254: rails [curve], s-ne [tile] terrain = rails icon = 263 attack = 2 defend = -3 move = 2 color = 50,34,4 # 255: rails [curve], nw-ne [tile] terrain = rails icon = 260 attack = 2 defend = -3 move = 2 color = 50,34,4 # 256: rails [curve], sw-se [tile] terrain = rails icon = 259 attack = 2 defend = -3 move = 2 color = 50,34,4 # 257: rails [junction], n-sw-se [tile] terrain = rails icon = 310 attack = 2 defend = -3 move = 2 color = 50,34,4 # 258: rails [junction], s-nw-ne [tile] terrain = rails icon = 311 attack = 2 defend = -3 move = 2 color = 50,34,4 # 259: rails [junction], n-s-sw [tile] terrain = rails icon = 285 attack = 2 defend = -3 move = 2 color = 50,34,4 # 260: rails [junction], n-s-se [tile] terrain = rails icon = 286 attack = 2 defend = -3 move = 2 color = 50,34,4 # 261: rails [junction], s-n-nw [tile] terrain = rails icon = 301 attack = 2 defend = -3 move = 2 color = 50,34,4 # 262: rails [junction], s-n-ne [tile] terrain = rails icon = 287 attack = 2 defend = -3 move = 2 color = 50,34,4 # 263: rails [junction], nw-se-s [tile] terrain = rails icon = 304 attack = 2 defend = -3 move = 2 color = 50,34,4 # 264: rails [junction], ne-sw-s [tile] terrain = rails icon = 305 attack = 2 defend = -3 move = 2 color = 50,34,4 # 265: rails [junction], sw-ne-n [tile] terrain = rails icon = 302 attack = 2 defend = -3 move = 2 color = 50,34,4 # 266: rails [junction], se-nw-n [tile] terrain = rails icon = 303 attack = 2 defend = -3 move = 2 color = 50,34,4 # 267: rails [junction], nw-se-ne [tile] terrain = rails icon = 306 attack = 2 defend = -3 move = 2 color = 50,34,4 # 268: rails [junction], ne-sw-nw [tile] terrain = rails icon = 307 attack = 2 defend = -3 move = 2 color = 50,34,4 # 269: rails [junction], sw-ne-se [tile] terrain = rails icon = 308 attack = 2 defend = -3 move = 2 color = 50,34,4 # 270: rails [junction], se-nw-sw [tile] terrain = rails icon = 309 attack = 2 defend = -3 move = 2 color = 50,34,4 # 271: rails [crossing], n-s-nw-se [tile] terrain = rails icon = 282 attack = 2 defend = -3 move = 2 color = 50,34,4 # 272: rails [crossing], n-s-ne-sw [tile] terrain = rails icon = 284 attack = 2 defend = -3 move = 2 color = 50,34,4 # 273: rails [crossing], nw-se-ne-sw [tile] terrain = rails icon = 283 attack = 2 defend = -3 move = 2 color = 50,34,4 # 274: rails [bridge], n-s [tile] terrain = rails icon = 292 attack = 2 defend = -3 move = 2 color = 50,34,4 # 275: rails [bridge], nw-se [tile] terrain = rails icon = 293 attack = 2 defend = -3 move = 2 color = 50,34,4 # 276: rails [bridge], ne-sw [tile] terrain = rails icon = 294 attack = 2 defend = -3 move = 2 color = 50,34,4 # 277: crossing: rails, n-s + road, nw-se [tile] terrain = road terrain = rails icon = 189 attack = 3 defend = -3 move = 1 color = 50,34,4 # 278: crossing: rails, n-s + road, ne-sw [tile] terrain = road terrain = rails icon = 190 attack = 3 defend = -3 move = 1 color = 50,34,4 # 279: crossing: rails, nw-se + road, n-s [tile] terrain = road terrain = rails icon = 191 attack = 3 defend = -3 move = 1 color = 50,34,4 # 280: crossing: rails, nw-se + road, ne-sw [tile] terrain = road terrain = rails icon = 213 attack = 3 defend = -3 move = 1 color = 50,34,4 # 281: crossing: rails, ne-sw + road, n-s [tile] terrain = road terrain = rails icon = 214 attack = 3 defend = -3 move = 1 color = 50,34,4 # 282: crossing: rails, ne-sw + road, nw-se [tile] terrain = road terrain = rails icon = 215 attack = 3 defend = -3 move = 1 color = 50,34,4 # 283: crossing: rails, n-s + path, nw-se [tile] terrain = road terrain = rails icon = 275 attack = 3 defend = -3 move = 2 color = 104,89,25 # 284: crossing: rails, n-s + path, ne-sw [tile] terrain = road terrain = rails icon = 276 attack = 3 defend = -3 move = 2 color = 104,89,25 # 285: crossing: rails, nw-se + path, n-s [tile] terrain = road terrain = rails icon = 277 attack = 3 defend = -3 move = 2 color = 104,89,25 # 286: crossing: rails, nw-se + path, ne-sw [tile] terrain = road terrain = rails icon = 278 attack = 3 defend = -3 move = 2 color = 104,89,25 # 287: crossing: rails, ne-sw + path, n-s [tile] terrain = road terrain = rails icon = 279 attack = 3 defend = -3 move = 2 color = 104,89,25 # 288: crossing: rails, ne-sw + path, nw-se [tile] terrain = road terrain = rails icon = 280 attack = 3 defend = -3 move = 2 color = 104,89,25 # 289: crossing: rails, n-s + river nw-se [tile] terrain = rails icon = 297 defend = -4 move = 2 color = 50,34,4 # 290: crossing: rails, n-s + river ne-sw [tile] terrain = rails icon = 299 defend = -4 move = 2 color = 50,34,4 # 291: crossing: rails, nw-se + river n-s [tile] terrain = rails icon = 295 defend = -4 move = 2 color = 50,34,4 # 292: crossing: rails, nw-se + river ne-sw [tile] terrain = rails icon = 300 defend = -4 move = 2 color = 50,34,4 # 293: crossing: rails, ne-sw + river n-s [tile] terrain = rails icon = 296 defend = -4 move = 2 color = 50,34,4 # 294: crossing: rails, ne-sw + river nw-se [tile] terrain = rails icon = 298 defend = -4 move = 2 color = 50,34,4 # 295: crossing: road, n-s + river, nw-se [tile] terrain = road icon = 235 attack = 3 defend = -4 move = 1 color = 50,34,4 # 296: crossing: road, n-s + river, ne-sw [tile] terrain = road icon = 237 attack = 3 defend = -4 move = 1 color = 50,34,4 # 297: crossing: road, nw-se + river, n-s [tile] terrain = road icon = 233 attack = 3 defend = -4 move = 1 color = 50,34,4 # 298: crossing: road, nw-se + river, ne-sw [tile] terrain = road icon = 238 attack = 3 defend = -4 move = 1 color = 50,34,4 # 299: crossing: road, ne-sw + river, n-s [tile] terrain = road icon = 234 attack = 3 defend = -4 move = 1 color = 50,34,4 # 300: crossing: road, ne-sw + river, nw-se [tile] terrain = road icon = 236 attack = 3 defend = -4 move = 1 color = 50,34,4 # 301: crossing: path, n-s + river, nw-se [tile] terrain = road icon = 266 attack = 3 defend = -4 move = 2 color = 104,89,25 # 302: crossing: path, n-s + river, ne-sw [tile] terrain = road icon = 268 attack = 3 defend = -4 move = 2 color = 104,89,25 # 303: crossing: path, nw-se + river, n-s [tile] terrain = road icon = 264 attack = 3 defend = -4 move = 2 color = 104,89,25 # 304: crossing: path, nw-se + river, ne-sw [tile] terrain = road icon = 269 attack = 3 defend = -4 move = 2 color = 104,89,25 # 305: crossing: path, ne-sw + river, n-s [tile] terrain = road icon = 265 attack = 3 defend = -4 move = 2 color = 104,89,25 # 306: crossing: path, ne-sw + river, nw-se [tile] terrain = road icon = 267 attack = 3 defend = -4 move = 2 color = 104,89,25 # 307: swamp [tile] terrain = swamp icon = 33 attack = -5 defend = -5 move = 4 color = 50,70,36 # 308: lake [tile] terrain = shallowwater icon = 288 move = 2 color = 86,150,188 # 309: river [straight], n-s [tile] terrain = shallowwater icon = 312 move = 2 color = 86,150,188 # 310: river [straight], nw-se [tile] terrain = shallowwater icon = 313 move = 2 color = 86,150,188 # 311: river [straight], ne-sw [tile] terrain = shallowwater icon = 314 move = 2 color = 86,150,188 # 312: river [curve], n-sw [tile] terrain = shallowwater icon = 317 move = 2 color = 86,150,188 # 313: river [curve], n-se [tile] terrain = shallowwater icon = 318 move = 2 color = 86,150,188 # 314: river [curve], s-nw [tile] terrain = shallowwater icon = 320 move = 2 color = 86,150,188 # 315: river [curve], s-ne [tile] terrain = shallowwater icon = 319 move = 2 color = 86,150,188 # 316: river [curve], nw-ne [tile] terrain = shallowwater icon = 316 move = 2 color = 86,150,188 # 317: river [curve], sw-se [tile] terrain = shallowwater icon = 315 move = 2 color = 86,150,188 # 318: river [junction], n-sw-se [tile] terrain = shallowwater icon = 327 move = 2 color = 86,150,188 # 319: river [junction], s-nw-ne [tile] terrain = shallowwater icon = 328 move = 2 color = 86,150,188 # 320: river [junction], sw-ne-n [tile] terrain = shallowwater icon = 329 move = 2 color = 86,150,188 # 321: river [junction], n-sw-s [tile] terrain = shallowwater icon = 336 move = 2 color = 86,150,188 # 322: river [junction], n-s-se [tile] terrain = shallowwater icon = 337 move = 2 color = 86,150,188 # 323: river [junction], nw-n-s [tile] terrain = shallowwater icon = 338 move = 2 color = 86,150,188 # 324: river [junction], n-ne-s [tile] terrain = shallowwater icon = 339 move = 2 color = 86,150,188 # 325: river [junction], nw-s-se [tile] terrain = shallowwater icon = 340 move = 2 color = 86,150,188 # 326: river [junction], ne-sw-s [tile] terrain = shallowwater icon = 341 move = 2 color = 86,150,188 # 327: river [junction], nw-n-se [tile] terrain = shallowwater icon = 342 move = 2 color = 86,150,188 # 328: river [junction], nw-ne-se [tile] terrain = shallowwater icon = 343 move = 2 color = 86,150,188 # 329: river [junction], nw-ne-sw [tile] terrain = shallowwater icon = 344 move = 2 color = 86,150,188 # 330: river [junction], ne-sw-se [tile] terrain = shallowwater icon = 345 move = 2 color = 86,150,188 # 331: river [junction], nw-sw-se [tile] terrain = shallowwater icon = 346 move = 2 color = 86,150,188 # 332: river [crossing], n-nw-s-se [tile] terrain = shallowwater icon = 347 move = 2 color = 86,150,188 # 333: river [crossing], n-ne-s-sw [tile] terrain = shallowwater icon = 348 move = 2 color = 86,150,188 # 334: river [crossing], nw-ne-sw-se [tile] terrain = shallowwater icon = 349 move = 2 color = 86,150,188 # 335: river [end], n-s [tile] terrain = shallowwater icon = 322 move = 2 color = 86,150,188 # 336: river [end], s-n [tile] terrain = shallowwater icon = 321 move = 2 color = 86,150,188 # 337: river [end], nw-se [tile] terrain = shallowwater icon = 324 move = 2 color = 86,150,188 # 338: river [end], ne-sw [tile] terrain = shallowwater icon = 326 move = 2 color = 86,150,188 # 339: river [end], sw-ne [tile] terrain = shallowwater icon = 325 move = 2 color = 86,150,188 # 340: river [end], se-nw [tile] terrain = shallowwater icon = 323 move = 2 color = 86,150,188 # 341: river [footbridge], n-s [tile] terrain = restricted icon = 289 attack = 3 defend = -3 move = 1 color = 104,89,25 # 342: river [footbridge], nw-se [tile] terrain = restricted icon = 290 attack = 3 defend = -3 move = 1 color = 104,89,25 # 343: river [footbridge], ne-sw [tile] terrain = restricted icon = 291 attack = 3 defend = -3 move = 1 color = 104,89,25 # 344: coast, n [tile] terrain = shallowwater icon = 360 move = 2 color = 86,150,188 # 345: coast, nw [tile] terrain = shallowwater icon = 365 move = 2 color = 86,150,188 # 346: coast, ne [tile] terrain = shallowwater icon = 361 move = 2 color = 86,150,188 # 347: coast, sw [tile] terrain = shallowwater icon = 364 move = 2 color = 86,150,188 # 348: coast, se [tile] terrain = shallowwater icon = 362 move = 2 color = 86,150,188 # 349: coast, s [tile] terrain = shallowwater icon = 363 move = 2 color = 86,150,188 # 350: coast, n-nw [tile] terrain = shallowwater icon = 366 move = 2 color = 86,150,188 # 351: coast, n-ne [tile] terrain = shallowwater icon = 367 move = 2 color = 86,150,188 # 352: coast, nw-sw [tile] terrain = shallowwater icon = 371 move = 2 color = 86,150,188 # 353: coast, ne-se [tile] terrain = shallowwater icon = 368 move = 2 color = 86,150,188 # 354: coast, s-sw [tile] terrain = shallowwater icon = 370 move = 2 color = 86,150,188 # 355: coast, s-se [tile] terrain = shallowwater icon = 369 move = 2 color = 86,150,188 # 356: coast, nw-n-ne [tile] terrain = shallowwater icon = 372 move = 2 color = 86,150,188 # 357: coast, n-nw-sw-s [tile] terrain = shallowwater icon = 375 move = 2 color = 86,150,188 # 358: coast, n-ne-se-s [tile] terrain = shallowwater icon = 373 move = 2 color = 86,150,188 # 359: coast, sw-s-se [tile] terrain = shallowwater icon = 374 move = 2 color = 86,150,188 # 360: water [shallow] [tile] terrain = shallowwater icon = 333 move = 2 color = 86,150,188 # 361: water [medium] [tile] terrain = water icon = 334 move = 2 color = 50,118,148 # 362: water [deep] [tile] terrain = deepwater icon = 335 move = 2 color = 44,98,119 # 363: cliffs [one] [tile] terrain = rough icon = 330 attack = -5 defend = -5 move = 3 color = 150,154,132 # 364: cliffs [two] [tile] terrain = rough icon = 331 attack = -5 defend = -5 move = 3 color = 150,154,132 # 365: cliffs [three] [tile] terrain = rough icon = 332 attack = -5 defend = -5 move = 3 color = 150,154,132 # 366: pier, west [tile] terrain = plains icon = 354 move = 2 color = 138,118,100 # 367: pier, east [tile] terrain = plains icon = 355 move = 2 color = 138,118,100 # 368: pier [crane], west [tile] terrain = plains icon = 352 move = 2 color = 138,118,100 # 369: pier [crane], east [tile] terrain = plains icon = 353 move = 2 color = 138,118,100 # 370: pier [jetty], n-s [tile] terrain = plains icon = 356 move = 2 color = 138,118,100 # 371: pier [jetty], south [tile] terrain = plains icon = 357 move = 2 color = 138,118,100 # 372: pier [jetty], north [tile] terrain = plains icon = 358 move = 2 color = 138,118,100 # 373: pier [dock], south [tile] terrain = shallowwater terrain = water icon = 350 move = 2 color = 86,150,188 # 374: pier [dock], north [tile] terrain = shallowwater terrain = water icon = 351 move = 2 color = 86,150,188 crimson-0.5.3/tools/cfed.cpp0000644000076400001440000020653010554366261012675 00000000000000/* cfed -- a level editor for Crimson Fields Copyright (C) 2000-2007 Jens Granseuer 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 (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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* cfed.cpp */ #include #include #include "SDL.h" #include "parser.h" #include "mission.h" #include "gamedefs.h" #include "strutil.h" #include "globals.h" #ifdef _MSC_VER // SDL_Main linkage destroys the command line in VS8 #undef main #endif #define MAX_MAP_WIDTH 180 #define MAX_MAP_HEIGHT 180 class CfedParserUtils { protected: CfedParserUtils( Mission &m ) : m(m) {} Point ParseCoords( const string &s ) const; const UnitType *ParseUnitName( const string &name ) const; int ParsePlayerId( const string &s ) const; Mission &m; }; class MissionHandler : public KeyValueSectionHandler { public: MissionHandler( Mission &m, string &tileset, string &unitset, Point &mapsize ); int ParseSection( ifstream &in, const string *opt, unsigned long &line ); private: Mission &m; string &tiles; string &units; Point &mapsize; }; class PlayerHandler : public KeyValueSectionHandler { public: PlayerHandler( Mission &m ) : m(m) {} int ParseSection( ifstream &in, const string *opt, unsigned long &line ); private: int ParseColor( const string &s, Color &col ) const; Mission &m; static int parsed; }; class UnitHandler : public KeyValueSectionHandler, public CfedParserUtils { public: UnitHandler( Mission &m ) : CfedParserUtils(m) {} int ParseSection( ifstream &in, const string *opt, unsigned long &line ); }; class ShopHandler : public KeyValueSectionHandler, public CfedParserUtils { public: ShopHandler( Mission &m ) : CfedParserUtils(m) {} int ParseSection( ifstream &in, const string *opt, unsigned long &line ); }; class EventHandler : public KeyValueSectionHandler, public CfedParserUtils { public: EventHandler( Mission &m ) : CfedParserUtils(m) {} int ParseSection( ifstream &in, const string *opt, unsigned long &line ); }; class MessagesHandler : public SectionHandler { public: MessagesHandler( Mission &m ) : m(m) {} int ParseSection( ifstream &in, const string *opt, unsigned long &line ); private: Mission &m; }; class MapHandler : public SectionHandler { public: MapHandler( Mission &m, const Point &size ) : m(m), size(size) {} int ParseSection( ifstream &in, const string *opt, unsigned long &line ); private: Mission &m; const Point &size; }; class MapRawHandler : public SectionHandler { public: MapRawHandler( Mission &m, const Point &size ) : m(m), size(size) {} int ParseSection( ifstream &in, const string *opt, unsigned long &line ); private: Mission &m; const Point &size; }; // parser helpers // parse positional information of the form "x/y" Point CfedParserUtils::ParseCoords( const string &s ) const { string val; Point pos; size_t sep = s.find( '/' ); if ( sep == string::npos ) return Point(-1,-1); val = s.substr( 0, sep ); CFParser::RemWhitespace( val ); if ( val.size() == 0 ) return Point(-1,-1); pos.x = CFParser::StrToNum( val ); val = s.substr( sep + 1 ); CFParser::RemWhitespace( val ); if ( val.size() == 0 ) return Point(-1,-1); pos.y = CFParser::StrToNum( val ); if ( (pos.x < 0) || (pos.y < 0) ) return Point(-1,-1); return pos; } // get the unit description from a unit name const UnitType *CfedParserUtils::ParseUnitName( const string &name ) const { const UnitType *ut = NULL; const UnitSet &us = m.GetUnitSet(); for ( int i = 0; i < us.NumTiles(); ++i ) { if ( name == us.UnitName(i) ) { ut = us.GetUnitInfo(i); break; } } return ut; } // turn the number given into a player id int CfedParserUtils::ParsePlayerId( const string &s ) const { int rc = -1; // this is different from internal use in the game! here 0 means no owner! int id = CFParser::StrToNum(s); if ( id == 1 ) rc = PLAYER_ONE; else if ( id == 2 ) rc = PLAYER_TWO; else if ( id == 0 ) rc = PLAYER_NONE; return rc; } // mission parser MissionHandler::MissionHandler( Mission &m, string &tileset, string &unitset, Point &mapsize ) : m(m), tiles(tileset), units(unitset), mapsize(mapsize) { tiles = "default"; units = "default"; mapsize = Point(-1, -1); } int MissionHandler::ParseSection( ifstream &in, const string *opt, unsigned long &line ) { int rc = KeyValueSectionHandler::ParseSection( in, opt, line ); if ( rc == 0 ) { for ( vector >::iterator it = pairs.begin(); it != pairs.end(); ++it ) { string key = (*it).first; string val = (*it).second; if ( key == "mapwidth" ) mapsize.x = StrToNum(val); // map width else if ( key == "mapheight" ) mapsize.y = StrToNum(val); // map height else if ( key == "name" ) m.SetName(StrToNum(val)); // map name else if ( key == "info" ) m.SetLevelInfoMsg(StrToNum(val)); // level info else if ( key == "campaignname" ) m.SetCampaignName(StrToNum(val)); // campaign name else if ( key == "campaigninfo" ) m.SetCampaignInfo(StrToNum(val)); // campaign info else if ( key == "players" ) m.SetNumPlayers(StrToNum(val)); // 1 or 2 player map else if ( key == "unitset" ) units = val; // unit set to use else if ( key == "tileset" ) tiles = val; // tile set to use else if ( key == "nextmap" ) m.SetSequel(val.c_str()); // next map else if ( key == "music" ) m.SetMusic(val.c_str()); // soundtrack else if ( key == "campaign" ) m.SetCampaign(StrToNum(val) != 0); // campaign map else if ( key == "skirmish" ) m.SetSkirmish(StrToNum(val) != 0); // suitable for skirmishes else { rc = 1; cerr << "Error near line " << line << ": Invalid keyword '" << key << "'" << endl; break; } } } return rc; } // player parser int PlayerHandler::parsed = 0; int PlayerHandler::ParseSection( ifstream &in, const string *opt, unsigned long &line ) { if ( parsed > 1 ) { cerr << "Error in line " << line << ": more than two [player] sections defined" << endl; return 1; } int rc = KeyValueSectionHandler::ParseSection( in, opt, line ); if ( rc == 0 ) { Player &p = m.GetPlayer( parsed ); for ( vector >::iterator it = pairs.begin(); it != pairs.end(); ++it ) { string key = (*it).first; string val = (*it).second; if ( key == "name" ) p.SetNameID(StrToNum(val)); else if ( key == "briefing" ) p.SetBriefing(StrToNum(val)); else if ( (key == "fcolor") || (key == "bcolor") ) { Color col; if ( !ParseColor(val, col) ) { if ( key[0] == 'f' ) p.SetLightColor(col); else p.SetDarkColor(col); } else { rc = 1; cerr << "Error near line " << line << ": Could not parse color '" << val << "'" << endl; } } else { rc = 1; cerr << "Error near line " << line << ": Invalid keyword '" << key << "' in this context" << endl; } } } ++parsed; return rc; } // parse a color defined as "rr,gg,bb" int PlayerHandler::ParseColor( const string &s, Color &col ) const { size_t pos1, pos2; short comp; string val; pos1 = s.find( ',' ); pos2 = s.rfind( ',' ); if ( pos1 == pos2 ) return -1; val = s.substr( 0, pos1 ); RemWhitespace( val ); if ( val.size() == 0 ) return -1; comp = StrToNum( val ); if ( (comp < 0) || (comp > 255) ) return -1; col.r = comp; val = s.substr( pos1 + 1, pos2 - pos1 - 1 ); RemWhitespace( val ); if ( val.size() == 0 ) return -1; comp = StrToNum( val ); if ( (comp < 0) || (comp > 255) ) return -1; col.g = comp; val = s.substr( pos2 + 1 ); RemWhitespace( val ); if ( val.size() == 0 ) return -1; comp = StrToNum( val ); if ( (comp < 0) || (comp > 255) ) return -1; col.b = comp; return 0; } // unit parser int UnitHandler::ParseSection( ifstream &in, const string *opt, unsigned long &line ) { int rc = KeyValueSectionHandler::ParseSection( in, opt, line ); if ( rc == 0 ) { Unit *u = new Unit(); u->SetPosition( Point(-1,-1) ); u->SetID( 0 ); u->UnsetFlags( ~0 ); u->SetOwner( PLAYER_NONE ); u->SetType( m.GetUnitSet().GetUnitInfo(0) ); u->SetCrystals( 0 ); u->SetDirection( 99 ); u->SetGroupSize( MAX_GROUP_SIZE ); u->SetXP( 0 ); for ( vector >::iterator it = pairs.begin(); (it != pairs.end()) && (rc == 0); ++it ) { string key = (*it).first; string val = (*it).second; if ( key == "pos" ) u->SetPosition(ParseCoords(val)); // unit position else if ( key == "id" ) u->SetID(StrToNum(val)); // unit id else if ( key == "crystals" ) u->SetCrystals(StrToNum(val)); // crystals else if ( key == "face" ) u->SetDirection(StrToNum(val)); // direction else if ( key == "size" ) u->SetGroupSize(StrToNum(val)); // group size else if ( key == "xp" ) u->SetXP(StrToNum(val) * XP_PER_LEVEL); // experience else if ( key == "type" ) { // unit type const UnitType *type = ParseUnitName( val ); if ( type ) { u->SetType( type ); u->SetFlags( u->Flags() | type->Flags() ); } else { rc = 1; cerr << "Error: Unknown unit type '" << val << "' near line " << line << endl; } } else if ( key == "player" ) { // owning player int id = ParsePlayerId( val ); if ( id != -1 ) u->SetOwner( id ); else { rc = 1; cerr << "Error near line " << line << ": Invalid owner of unit" << endl; } } else { rc = 1; cerr << "Error near line " << line << ": Invalid keyword '" << key << "' in this context" << endl; } } if ( rc == 0 ) m.GetUnits().AddTail( u ); else delete u; } return rc; } // shop parser int ShopHandler::ParseSection( ifstream &in, const string *opt, unsigned long &line ) { int rc = KeyValueSectionHandler::ParseSection( in, opt, line ); if ( rc == 0 ) { Building *b = new Building(); b->SetPosition( Point(0,0) ); b->SetID( 0 ); b->SetOwner( PLAYER_NONE ); b->SetCrystalProduction( 0 ); b->SetCrystals( 0 ); b->SetMaxCrystals( 1000 ); b->SetUnitProduction( 0 ); b->UnsetFlags( ~0 ); b->SetMinWeight( 0 ); b->SetMaxWeight( 99 ); b->SetNameID( -1 ); for ( vector >::iterator it = pairs.begin(); (it != pairs.end()) && (rc == 0); ++it ) { string key = (*it).first; string val = (*it).second; if ( key == "pos" ) b->SetPosition(ParseCoords(val)); // shop position else if ( key == "id" ) b->SetID(StrToNum(val)); // ID else if ( key == "name" ) b->SetNameID(StrToNum(val)); // name else if ( key == "mining" ) b->SetCrystalProduction(StrToNum(val)); // mining else if ( key == "crystals" ) b->SetCrystals(StrToNum(val)); // resources else if ( key == "capacity" ) b->SetMaxCrystals(StrToNum(val)); // max. resources else if ( key == "minweight" ) b->SetMinWeight(StrToNum(val)); // smallest unit allowed else if ( key == "maxweight" ) b->SetMaxWeight(StrToNum(val)); // biggest unit allowed else if ( key == "player" ) { // owning player int id = ParsePlayerId( val ); if ( id != -1 ) b->SetOwner( id ); else { rc = 1; cerr << "Error near line " << line << ": Invalid owner of building" << endl; } } else if ( key == "type" ) { // type if ( val == "workshop" ) b->SetFlags(BLD_WORKSHOP); else if ( val == "factory" ) b->SetFlags(BLD_FACTORY); else { rc = 1; cerr << "Error in line " << line << ": Unknown type '" << val << "'" << endl; } } else if ( key == "factory" ) { // can produce which units const UnitType *type = ParseUnitName( val ); if ( type ) { b->SetFlags(BLD_FACTORY); b->SetUnitProduction(b->UnitProduction()|(1<ID())); } else { rc = 1; cerr << "Error: Unknown unit type '" << val << "' near line " << line << endl; } } else { rc = 1; cerr << "Error near line " << line << ": Invalid keyword '" << key << "' in this context" << endl; } } if ( rc == 0 ) m.GetBuildings().AddTail( b ); else delete b; } return rc; } // event parser int EventHandler::ParseSection( ifstream &in, const string *opt, unsigned long &line ) { int rc = KeyValueSectionHandler::ParseSection( in, opt, line ); if ( rc == 0 ) { Event *e = new Event(); e->SetID( 0 ); e->SetType( 99 ); e->SetPlayer( PLAYER_NONE ); e->SetTrigger( 99 ); e->SetDependency( -1 ); e->SetDiscard( -1 ); for ( int i = 0; i < 3; ++i ) { e->SetData( i, -9999 ); e->SetTData( i, -9999 ); } e->SetTitle( -1 ); e->SetMessage( -1 ); e->SetFlags( 0 ); e->SetTmpBuf( "" ); for ( vector >::iterator it = pairs.begin(); (it != pairs.end()) && (rc == 0); ++it ) { string key = (*it).first; string val = (*it).second; Point loc; int id; if ( key == "type" ) { // type if ( val == "message" ) e->SetType(EVENT_MESSAGE); else if ( val == "research" ) e->SetType(EVENT_RESEARCH); else if ( val == "mining" ) e->SetType(EVENT_MINING); else if ( val == "score" ) e->SetType(EVENT_SCORE); else if ( val == "configure" ) e->SetType(EVENT_CONFIGURE); else if ( val == "manipulateevent" ) e->SetType(EVENT_MANIPULATE_EVENT); else if ( val == "sethex" ) e->SetType(EVENT_SET_HEX); else if ( val == "settimer" ) e->SetType(EVENT_SET_TIMER); else if ( val == "destroyunit" ) e->SetType(EVENT_DESTROY_UNIT); else if ( val == "createunit" ) { e->SetType(EVENT_CREATE_UNIT); e->SetData(2, NORTH | (MAX_GROUP_SIZE << 3) | (0 << 6)); // dir, group size, xp defaults } else { rc = 1; cerr << "Error near line " << line << ": Unknown event type '" << val << "'" << endl; } } else if ( key == "trigger" ) { // trigger if ( val == "timer" ) e->SetTrigger(ETRIGGER_TIMER); else if ( val == "unitdestroyed" ) e->SetTrigger(ETRIGGER_UNIT_DESTROYED); else if ( val == "unitposition" ) e->SetTrigger(ETRIGGER_UNIT_POSITION); else if ( val == "handicap" ) e->SetTrigger(ETRIGGER_HANDICAP); else if ( val == "havecrystals" ) e->SetTrigger(ETRIGGER_HAVE_CRYSTALS); else if ( val == "havebuilding" ) { e->SetTrigger(ETRIGGER_HAVE_BUILDING); e->SetTData(2, -1); } else if ( val == "haveunit" ) { e->SetTrigger(ETRIGGER_HAVE_UNIT); e->SetTData(2, -1); } else { rc = 1; cerr << "Error near line " << line << ": Unknown trigger type '" << val << "'" << endl; } } else if ( key == "player" ) { // player id = ParsePlayerId( val ); if ( (id != -1) && (id != PLAYER_NONE) ) e->SetPlayer( id ); else { rc = 1; cerr << "Error near line " << line << ": Invalid player" << endl; } } else if ( key == "title" ) e->SetTitle(StrToNum(val)); else if ( key == "message" ) e->SetMessage(StrToNum(val)); else if ( key == "id" ) e->SetID(StrToNum(val)); else if ( key == "flags" ) e->SetFlags(StrToNum(val)); else if ( key == "depend" ) e->SetDependency(StrToNum(val)); else if ( key == "discard" ) e->SetDiscard(StrToNum(val)); else { if ( (e->Trigger() == ETRIGGER_TIMER) && (key == "ttime") ) e->SetTData(0, StrToNum(val)); // timer else if ( (e->Trigger() == ETRIGGER_HAVE_UNIT) && (key == "tunit") ) e->SetTData(0, StrToNum(val)); // unit else if ( (e->Trigger() == ETRIGGER_HAVE_BUILDING) && (key == "tbuilding") ) e->SetTData(0, StrToNum(val)); // building id else if ( (e->Trigger() == ETRIGGER_HAVE_CRYSTALS) && (key == "tbuilding") ) e->SetTData(2, StrToNum(val)); // building id else if ( (e->Trigger() == ETRIGGER_HAVE_CRYSTALS) && (key == "tcrystals") ) e->SetTData(0, StrToNum(val)); // number of crystals else if ( ((e->Trigger() == ETRIGGER_HAVE_BUILDING) || (e->Trigger() == ETRIGGER_HAVE_UNIT) || (e->Trigger() == ETRIGGER_UNIT_DESTROYED) || (e->Trigger() == ETRIGGER_HAVE_CRYSTALS)) && (key == "towner") ) e->SetTData(1, StrToNum(val)-1); // owner of shop/unit else if ( (e->Trigger() == ETRIGGER_UNIT_POSITION) && (key == "towner") ) e->SetTData(2, StrToNum(val)-1); // owner of unit else if ( ((e->Trigger() == ETRIGGER_HAVE_BUILDING) || (e->Trigger() == ETRIGGER_HAVE_UNIT)) && (key == "ttime") ) e->SetTData(2, StrToNum(val)); // time after which to check else if ( (e->Trigger() == ETRIGGER_UNIT_POSITION) && (key == "tpos") ) { loc = ParseCoords(val); if ( (loc.x < 0) || (loc.y < 0) ) { rc = 1; cerr << "Error near line " << line << ": Invalid position " << val << endl; } else e->SetTData(1, m.GetMap().Hex2Index(loc)); // unit position } else if ( ((e->Trigger() == ETRIGGER_UNIT_POSITION) || (e->Trigger() == ETRIGGER_UNIT_DESTROYED)) && (key == "tunit") ) { // unit or unit type if ( val.size() > 0 && (val[0] == '-' || isdigit(val[0])) ) { // seems to be a number -> specific unit or -1 e->SetTData(0, StrToNum(val)); } else { const UnitType *type = ParseUnitName( val ); if ( type ) e->SetTData(0, -type->ID() - 2); else { rc = 1; cerr << "Error near line " << line << ": Invalid unit type " << val << endl; } } } else if ( (e->Trigger() == ETRIGGER_HANDICAP) && (key == "thandicap") ) e->SetTData(0, StrToNum(val)); // handicap else switch ( e->Type() ) { case EVENT_CREATE_UNIT: // e_data[2] is split: 0000000 111 111 111 // xp size dir if ( key == "face" ) // direction to face e->SetData(2, (e->GetData(2) & ~0x0007) | (StrToNum(val) & 0x0007)); else if ( key == "size" ) // group size e->SetData(2, (e->GetData(2) & ~0x0038) | ((StrToNum(val) << 3) & 0x0038)); else if ( key == "xp" ) // experience e->SetData(2, (e->GetData(2) & ~0x01C0) | ((StrToNum(val) << 6) & 0x01C0)); else if ( key == "pos" ) { // where to create loc = ParseCoords(val); if ( (loc.x < 0) || (loc.y < 0) ) { rc = 1; cerr << "Error near line " << line << ": Invalid position " << val << endl; } else e->SetData(1, m.GetMap().Hex2Index(loc)); } else if ( key == "unit" ) { // unit type const UnitType *type = ParseUnitName( val ); if ( type ) e->SetData(0, type->ID()); else { rc = 1; cerr << "Error: Unknown unit type '" << val << "' near line " << line << endl; } } else { rc = 1; cerr << "Error near line " << line << ": Invalid keyword '" << key << "'" << endl; } break; case EVENT_DESTROY_UNIT: if ( key == "unit" ) e->SetData(0, StrToNum(val)); // id of unit to destroy else if ( key == "owner" ) e->SetData(1, StrToNum(val)-1); // unit owner else if ( key == "pos" ) { // unit position loc = ParseCoords(val); if ( (loc.x < 0) || (loc.y < 0) ) { rc = 1; cerr << "Error near line " << line << ": Invalid position " << val << endl; } else e->SetData(2, m.GetMap().Hex2Index(loc)); } break; case EVENT_MANIPULATE_EVENT: if ( key == "event" ) e->SetData(0, StrToNum(val)); // ID of target event else if ( key == "eflags" ) e->SetData(1, StrToNum(val)); // flags to modify else if ( key == "action" ) e->SetData(2, StrToNum(val)); // set/clear/toggle else { rc = 1; cerr << "Error near line " << line << ": Invalid keyword '" << key << "'" << endl; } break; case EVENT_MESSAGE: if ( key == "pos" ) { // hex to focus on loc = ParseCoords(val); if ( (loc.x < 0) || (loc.y < 0) ) { rc = 1; cerr << "Error near line " << line << ": Invalid position " << val << endl; } else e->SetData(0, m.GetMap().Hex2Index(loc)); } else { rc = 1; cerr << "Error near line " << line << ": Invalid keyword '" << key << "'" << endl; } break; case EVENT_MINING: if ( key == "building" ) e->SetData(0, StrToNum(val)); // id of building else if ( key == "crystals" ) e->SetData(1, StrToNum(val)); // crystal production else if ( key == "action" ) e->SetData(2, StrToNum(val)); // modification action else { rc = 1; cerr << "Error near line " << line << ": Invalid keyword '" << key << "'" << endl; } break; case EVENT_CONFIGURE: if ( key == "setting" ) { // setting to change if ( val == "briefing1" ) e->SetData(0, 0); else if ( val == "briefing2" ) e->SetData(0, 1); else if ( val == "nextmap" ) e->SetData(0, 2); } else if ( key == "value" ) { // value to set switch (e->GetData(0)) { case 0: case 1: e->SetData(1, StrToNum(val)); break; case 2: e->SetTmpBuf(val); break; default: rc = 1; cerr << "Error near line " << line << ": Configure event does not support value before setting" << endl; } } else { rc = 1; cerr << "Error near line " << line << ": Invalid keyword '" << key << "'" << endl; } break; case EVENT_RESEARCH: if ( key == "building" ) e->SetData(0, StrToNum(val)); // id of building else if ( key == "action" ) e->SetData(2, StrToNum(val)); // whether to allow or disallow this unit else if ( key == "unit" ) { // unit type const UnitType *type = ParseUnitName( val ); if ( type ) e->SetData(1, type->ID()); else { rc = 1; cerr << "Error: Unknown unit type '" << val << "' near line " << line << endl; } } else { rc = 1; cerr << "Error near line " << line << ": Invalid keyword '" << key << "'" << endl; } break; case EVENT_SCORE: if ( key == "success" ) e->SetData(0, StrToNum(val)); // success rate increase else if ( key == "othermsg" ) e->SetData(1, StrToNum(val)); // message for other player else if ( key == "othertitle" ) e->SetData(2, StrToNum(val)); // message title for other player else { rc = 1; cerr << "Error near line " << line << ": Invalid keyword '" << key << "'" << endl; } break; case EVENT_SET_HEX: if ( key == "pos" ) { // hex to change loc = ParseCoords(val); if ( (loc.x < 0) || (loc.y < 0) ) { rc = 1; cerr << "Error near line " << line << ": Invalid position " << val << endl; } else e->SetData(1, m.GetMap().Hex2Index(loc)); } else if ( key == "tile" ) { // new terrain type id = StrToNum(val); if ( (id < m.GetTerrainSet().NumTiles()) && (id >= 0) ) e->SetData(0, id); else { rc = 0; cerr << "Error: Invalid tile '" << val << "' near line " << line << endl; } } else { rc = 1; cerr << "Error near line " << line << ": Invalid keyword '" << key << "'" << endl; } break; case EVENT_SET_TIMER: if ( key == "event" ) e->SetData(0, StrToNum(val)); // ID of target event else if ( key == "time" ) e->SetData(1, StrToNum(val)); // time else if ( key == "offset" ) e->SetData(2, StrToNum(val)); // time offset else { rc = 1; cerr << "Error near line " << line << ": Invalid keyword '" << key << "'" << endl; } break; } } } if ( rc == 0 ) m.GetEvents().AddTail( e ); else delete e; } return rc; } // map parser int MapHandler::ParseSection( ifstream &in, const string *opt, unsigned long &line ) { if ( (size.x <= 0) || (size.x > MAX_MAP_WIDTH) || (size.y <= 0) || (size.y > MAX_MAP_HEIGHT) ) { cerr << "Error: Invalid map size" << endl; return -1; } m.GetMap().SetSize( size ); string buf; for ( int i = 0; i < size.y; ++i ) { getline( in, buf ); ++line; if ( in.eof() ) { cerr << "Error in line " << line << ": Unexpected end of map data" << endl; return -1; } RemWhitespace( buf ); if ( buf.size() != (unsigned short)size.x ) { cerr << "Error in line " << line << ": map row " << i << " does not comply with width parameter" << endl; return -1; } for ( int j = 0; j < size.x; ++j ) { unsigned short terrain; switch ( buf[j] ) { case '.': terrain = 30; break; /* plains */ case '*': terrain = 81; break; /* forest */ case '%': terrain = 117; break; /* mountains */ case '=': terrain = 360; break; /* shallow water */ case '~': terrain = 361; break; /* water */ case '-': terrain = 362; break; /* deep water */ case '1': terrain = 6; break; /* yellow hq entrance, east */ case '2': terrain = 7; break; /* blue hq entrance, east */ case '0': terrain = 8; break; /* neutral hq entrance, east */ case '<': terrain = 22; break; /* hq, left part */ case '>': terrain = 23; break; /* hq, right part */ case '^': terrain = 21; break; /* hq, upper part */ case 'v': terrain = 24; break; /* hq, lower part */ case '#': terrain = 307; break; /* swamp */ case '\\': terrain = 177; break; /* road, se-nw */ case '|': terrain = 176; break; /* road, s-n */ case '/': terrain = 178; break; /* road, sw-ne */ case 'y': terrain = 193; break; /* road, sw-n-ne */ case 'Y': terrain = 194; break; /* road, se-n-nw */ case '(': terrain = 180; break; /* road, n-se */ case ')': terrain = 179; break; /* road, n-sw */ case ']': terrain = 181; break; /* road, nw-s */ case '[': terrain = 182; break; /* road, ne-s */ case 'n': terrain = 184; break; /* road, sw-se */ case 'u': terrain = 183; break; /* road, nw-ne */ case 'o': terrain = 201; break; /* road, sw-nw-ne-se */ case 'k': terrain = 192; break; /* road, sw-s-ne */ case 'K': terrain = 191; break; /* road, s-se-nw */ case 'T': terrain = 188; break; /* road, n-s-se */ case 'U': terrain = 187; break; /* road, n-s-sw */ case 'V': terrain = 198; break; /* road, n-s-ne */ case 'W': terrain = 189; break; /* road, n-s-nw */ case 'X': terrain = 199; break; /* road, s-se-nw-n */ case 'x': terrain = 200; break; /* road, s-sw-ne-n */ case '!': terrain = 208; break; /* bridge, n-s */ case '`': terrain = 209; break; /* bridge, se-nw */ case '\'': terrain = 210; break; /* bridge, sw-ne */ case '4': terrain = 9; break; /* yellow depot entrance */ case '5': terrain = 10; break; /* blue depot entrance */ case '3': terrain = 11; break; /* neutral depot entrance */ case '7': terrain = 12; break; /* yellow factory entrance, north */ case '8': terrain = 13; break; /* blue factory entrance, north */ case '6': terrain = 14; break; /* neutral factory entrance, north */ case 'a': terrain = 144; break; /* wire fence, se-nw end */ case 'b': terrain = 147; break; /* wire fence, nw-se end */ case 'c': terrain = 146; break; /* wire fence, ne-sw end */ case 'd': terrain = 145; break; /* wire fence, sw-ne end */ case 'e': terrain = 133; break; /* wire fence, n-s */ case 'f': terrain = 135; break; /* wire fence, sw-ne */ case 'g': terrain = 134; break; /* wire fence, nw-se */ case 'h': terrain = 138; break; /* wire fence, nw-s */ case 'i': terrain = 139; break; /* wire fence, ne-s */ case 'j': terrain = 136; break; /* wire fence, sw-n */ case 'l': terrain = 137; break; /* wire fence, se-n */ case 'm': terrain = 140; break; /* wire fence, nw-ne */ case 'p': terrain = 141; break; /* wire fence, sw-se */ case '"': terrain = 363; break; /* cliff 1 */ case 'A': terrain = 18; break; /* yellow city */ case 'B': terrain = 19; break; /* blue city */ case 'C': terrain = 20; break; /* neutral city */ case 'D': terrain = 3; break; /* yellow hq entrance, west */ case 'E': terrain = 4; break; /* blue hq entrance, west */ case 'F': terrain = 5; break; /* neutral hq entrance, west */ case 'G': terrain = 0; break; /* yellow hq entrance, north */ case 'H': terrain = 1; break; /* blue hq entrance, north */ case 'I': terrain = 2; break; /* neutral hq entrance, north */ case '9': terrain = 15; break; /* yellow factory entrance, east */ case 'J': terrain = 16; break; /* blue factory entrance, east */ case 'L': terrain = 17; break; /* neutral factory entrance, east */ default: cerr << "Error in line " << line << ": Invalid character '" << buf[j] << "' in map" << endl; return -1; } m.GetMap().SetHexType( j, i, terrain ); } } return 0; } // read a raw map; a hex is represented by its ID and hexes are separated by comma; // this way, much more hex types can be accessed than by using plain ASCII code int MapRawHandler::ParseSection( ifstream &in, const string *opt, unsigned long &line ) { string buf; unsigned short terrain; size_t pos; const char *start; Map &map = m.GetMap(); if ( (size.x <= 0) || (size.x > MAX_MAP_WIDTH) || (size.y <= 0) || (size.y > MAX_MAP_HEIGHT) ) { cerr << "Error: Invalid map size" << endl; return -1; } map.SetSize( size ); for ( int i = 0; i < size.y; ++i ) { getline(in, buf); ++line; if ( in.eof() ) { cerr << "Error in line " << line << ": unexpected end of map data" << endl; return -1; } RemWhitespace( buf ); pos = 0; for ( int j = 0; j < size.x; ++j ) { start = &buf.c_str()[pos]; while ( (buf[pos] >= '0') && (buf[pos] <= '9') ) ++pos; if ( (buf[pos] == ',') || (buf[pos] == '\0') ) { buf[pos++] = '\0'; terrain = atoi( start ); if ( terrain >= map.GetTerrainSet()->NumTiles() ) { cerr << "Error in line " << line << ": Invalid hex id '" << terrain << "'" << endl; return -1; } else map.SetHexType( j, i, terrain ); } else { cerr << "Error in line " << line << ": Invalid character '" << buf[pos] << "' in map" << endl; return -1; } } } return 0; } // messages parser int MessagesHandler::ParseSection( ifstream &in, const string *opt, unsigned long &line ) { if ( !opt ) { cerr << "Error in line " << line << ": [Messages] section does not specify a language" << endl; return -1; } else if ( opt->size() != 2 ) { cerr << "Error in line " << line << ": " << *opt << " is not a valid language" << endl; return -1; } Language lang; lang.SetID( opt->c_str() ); string buf, msg; bool done = false; do { getline(in, buf); ++line; RemWhitespace( buf ); if ((buf.size() > 0) && ((buf[0] == '%') || (buf.substr(0,11) == "[/messages]"))) { // save last message if (!msg.empty()) { lang.AddMsg(msg); msg.erase(); } if (buf.substr(0,11) == "[/messages]") done = true; } else { if (!msg.empty()) msg += '\n'; msg.append(buf); } } while ( !in.eof() && !done ); int rc; if (in.eof()) { rc = -1; cerr << "Error in line " << line << ": Messages section unterminated" << endl; } else { rc = 0; m.GetMessages().AddLanguage( lang ); } return rc; } #define SECTION_MAP (1<<0) #define SECTION_MISSION (1<<1) #define SECTION_PLAYER1 (1<<2) #define SECTION_PLAYER2 (1<<3) class MissionParser : public Mission, public SectionParsedCallback { public: MissionParser( void ); int parse( const char *file ); int load_unit_set( const char *set ); int load_tile_set( const char *set ); void SectionParsed( const string §ion, SectionHandler &handler, CFParser &parser ); private: int check_game( void ) const; int check_units( void ) const; int check_shops( void ) const; int check_events( void ) const; int check_player( const Player &p ) const; template bool find_dups( const List &l, const T *o1 ) const { int cnt = 0; for ( T *o2 = static_cast(l.Head()); o2; o2 = static_cast(o2->Next()) ) { if ( o1->ID() == o2->ID() ) ++cnt; } return cnt > 1; } unsigned short parsed_sections; }; // start of program functions int main( int argc, char **argv ) { short show_help = 0; int rc, i; char *uset = NULL, *tset = NULL; string outname; if ( SDL_Init( 0 ) < 0 ) { cerr << "Error: Could not init SDL. " << SDL_GetError() << endl; return 1; } if ( argc < 2 ) show_help = 1; else { for ( i = argc - 1; i > 1; --i ) { if (strcmp(argv[i], "--help") == 0) show_help = 1; else if (strcmp(argv[i], "--version") == 0) { cout << "cfed "VERSION << endl; return 0; } else if (strcmp(argv[i-1], "--units") == 0) uset = argv[i]; else if (strcmp(argv[i-1], "--tiles") == 0) tset = argv[i]; else if (strcmp(argv[i-1], "-o") == 0) outname = argv[i]; } } if ( !uset || !tset ) show_help = 1; if ( show_help ) { cout << "Usage: " << argv[0] << " file --tiles --units [-o ]" << endl << " --help display this help and exit" << endl << " --version output version information and exit" << endl; return 0; } MissionParser parser; if ( parser.load_unit_set( uset ) != 0 ) { cerr << "Unable to open unit set " << uset << endl; return 1; } if ( parser.load_tile_set( tset ) != 0 ) { cerr << "Unable to open tile set " << tset << endl; return 1; } rc = parser.parse( argv[1] ); if ( !rc ) { if ( outname.empty() ) { outname = argv[1]; size_t pos = outname.rfind( '.' ); if ( pos != string::npos ) outname.erase( pos ); outname.append( ".lev" ); } rc = parser.Save( outname.c_str() ); if ( rc ) cerr << "Error opening file '" << outname << "' for writing" << endl; } return rc; } /* parse the file and create a binary mission file from it */ int MissionParser::parse( const char *file ) { parsed_sections = 0; int err = 0; Point mapsize; string tileset, unitset; CFParser parser; SectionHandler *h; parser.AddHandler( "mission", new MissionHandler( *this, tileset, unitset, mapsize ) ); parser.AddHandler( "player", new PlayerHandler( *this ) ); parser.AddHandler( "messages", new MessagesHandler( *this ) ); h = new UnitHandler( *this ); h->SetEnabled( false ); parser.AddHandler( "unit", h ); h = new ShopHandler( *this ); h->SetEnabled( false ); parser.AddHandler( "building", h ); h = new EventHandler( *this ); h->SetEnabled( false ); parser.AddHandler( "event", h ); h = new MapHandler( *this, mapsize ); h->SetEnabled( false ); parser.AddHandler( "map", h ); h = new MapRawHandler( *this, mapsize ); h->SetEnabled( false ); parser.AddHandler( "map-raw", h ); parser.SetCallback( this ); err = parser.Parse( file ); if ( !err ) { if ( (parsed_sections & SECTION_MISSION) == 0 ) { cerr << "Error: Basic mission definitions missing" << endl; err = 1; } else if ( (parsed_sections & SECTION_MAP) == 0 ) { cerr << "Error: No map" << endl; err = 1; } } if ( !err && !messages.SetDefaultLanguage( CF_LANG_DEFAULT ) ) { cerr << "Error: No english language data found" << endl; err = 1; } if ( !err ) err = check_game(); if ( !err ) err = check_shops(); if ( !err ) err = check_units(); if ( !err ) err = check_events(); if ( !err ) err = check_player( p1 ); if ( !err ) err = check_player( p2 ); return err; } // mission parser callback called when a section has been // successfully parsed void MissionParser::SectionParsed( const string §ion, SectionHandler &handler, CFParser &parser ) { if ( section == "mission" ) { parsed_sections |= SECTION_MISSION; handler.SetEnabled( false ); parser.EnableHandler( "unit", true ); parser.EnableHandler( "building", true ); parser.EnableHandler( "event", true ); parser.EnableHandler( "map", true ); parser.EnableHandler( "map-raw", true ); } else if ( section == "player" ) { if ( parsed_sections & SECTION_PLAYER1 ) { parsed_sections |= SECTION_PLAYER2; handler.SetEnabled( false ); } else parsed_sections |= SECTION_PLAYER1; } else if ( (section == "map") || (section == "map-raw") ) { parsed_sections |= SECTION_MAP; parser.EnableHandler( "map", false ); parser.EnableHandler( "map-raw", false ); } } /* check game data for validity */ int MissionParser::check_game( void ) const { if ( (map.Width() == 0) || (map.Height() == 0) ) { cerr << "Error: map width or height is 0" << endl; return 1; } if ( GetName() == -1 ) { cerr << "Error: Level has not been given a name" << endl; return 1; } else if ( !GetMessage(GetName()) ) { cerr << "Error: Invalid mission name " << (short)GetName() << endl; return 1; } else { short num_msgs = -1, cnt; for ( std::map::const_iterator it = messages.GetLibrary().begin(); it != messages.GetLibrary().end(); ++it ) { const Language *lang = &it->second; // count messages for ( cnt = 0; lang->GetMsg(cnt); ++cnt ); // empty loop if ( num_msgs == -1 ) num_msgs = cnt; else if ( num_msgs != cnt ) { cerr << "Error: Language data for '" << lang->ID() << "' contains " << cnt << " messages." << endl << " Previous languages had " << num_msgs << endl; return 1; } if ( strlen(lang->GetMsg(GetName())) > 30 ) { cerr << "Error (" << lang->ID() << "): Mission name must not exceed 30 characters" << endl; return 1; } } } if ( (GetLevelInfoMsg() != -1) && !GetMessage(GetLevelInfoMsg()) ) { cerr << "Error: Invalid level info message " << (short)GetLevelInfoMsg() << endl; return 1; } if ( (GetCampaignName() != -1) && !GetMessage(GetCampaignName()) ) { cerr << "Error: Invalid campaign name " << (short)GetCampaignName() << endl; return 1; } if ( (GetCampaignInfo() != -1) && !GetMessage(GetCampaignInfo()) ) { cerr << "Error: Invalid campaign info message " << (short)GetCampaignInfo() << endl; return 1; } if ( !IsCampaign() && (GetSequel() || (GetCampaignName() != -1) || (GetCampaignInfo() != -1)) ) { cerr << "Error: Campaign settings found but map is not marked as a campaign map" << endl; return 1; } return 0; } /* check players */ int MissionParser::check_player( const Player &p ) const { short msg = p.NameID(); if ( msg == -1 ) { cerr << "Error: No name set for player" << endl; return 1; } else { if ( !GetMessage(msg) ) { cerr << "Error: Invalid name " << msg << " for player" << endl; return 1; } else { for ( std::map::const_iterator it = messages.GetLibrary().begin(); it != messages.GetLibrary().end(); ++it ) { const Language *lang = &it->second; if ( strlen(lang->GetMsg(msg)) > 30 ) { cerr << "Error(" << lang->ID() << "): Player names must not be longer than 30 characters" << endl; return 1; } } } } msg = p.Briefing(); if ( (msg != -1) && !GetMessage(msg) ) { cerr << "Error: Invalid briefing " << msg << " for player" << endl; return 1; } return 0; } /* check units */ int MissionParser::check_units( void ) const { Unit *u, *walk; /* if there is a transport at a unit's location put it in if possible */ /* this only checks for transports appearing before the actual unit in the file */ for ( u = static_cast(units.Head()); u; u = static_cast(u->Next()) ) { if ( (u->Position().x >= map.Width()) || (u->Position().y >= map.Height()) || (u->Position().x < 0) || (u->Position().y < 0) ) { cerr << "Error: unit (ID: " << u->ID() << ") out of map" << endl; return 1; } if ( u->IsTransport() && !u->IsSheltered() ) { if ( StorageLeft( *u ) < 0 ) { cerr << "Error: Unit " << u->Name() << " at " << u->Position().x << '/' << u->Position().y << " carries too many units or crystals" << endl; return 1; } for ( walk = static_cast(u->Next()); walk; walk = static_cast(walk->Next()) ) { if ( walk->Position() == u->Position() ) { if ( walk->Weight() > u->MaxWeight() ) { cerr << "Error: Unit " << walk->Name() << " too heavy for transport at " << u->Position().x << '/' << u->Position().y << endl; return 1; } else if ( walk->Weight() < u->MinWeight() ) { cerr << "Error: Unit " << walk->Name() << " too light for transport at " << u->Position().x << '/' << u->Position().y << endl; return 1; } else walk->SetFlags( U_SHELTERED ); } } } if ( u->Crystals() && !u->IsTransport() ) cerr << "Error: non-transport unit (ID: " << u->ID() << ") cannot carry crystals" << endl; for ( walk = static_cast(u->Next()); walk; walk = static_cast(walk->Next()) ) { if ( u->ID() == walk->ID() ) { cerr << "Error: Two or more units sharing one ID (" << u->ID() << ')' << endl; return 1; } if ( (u->Position() == walk->Position()) && (!(u->IsSheltered() || u->IsTransport()) || !walk->IsSheltered()) ) { cerr << "Error: Two or more units sharing one hex (" << u->Position().x << '/' << u->Position().y << ')' << endl; if ( walk->IsTransport() ) cerr << " This might be caused by a transport being declared after a unit it carries." << endl; return 1; } } if ( (u->Owner() == PLAYER_ONE) || (u->Owner() == PLAYER_TWO) ) { if ( u->GetDirection() == 99 ) u->SetDirection( u->Owner() * 3 ); /* direction not set, use defaults */ else if ( u->GetDirection() > NORTHWEST ) { cerr << "Error: Invalid direction " << (short)u->GetDirection() << " for unit (ID: " << u->ID() << ')' << endl; return 1; } } else { u->SetDirection( 0 ); if ( !(u->Owner() == PLAYER_NONE) || !u->IsSheltered() ) { cerr << "Error: unit with ID " << u->ID() << " has no legal owner" << endl; return 1; } } if ( (u->GroupSize() > MAX_GROUP_SIZE) || (u->GroupSize() == 0) ) { cerr << "Error: unit with ID " << u->ID() << " has size " << (short)u->GroupSize() << endl; return 1; } if ( u->XPLevel() > XP_MAX_LEVEL ) { cerr << "Error: unit with ID " << u->ID() << " has been promoted to XP level " << (short)u->XPLevel() << ", maximum is " << XP_MAX_LEVEL << endl; return 1; } } return 0; } /* check buildings */ int MissionParser::check_shops( void ) const { Building *b, *walk; Unit *u; for ( b = static_cast(buildings.Head()); b; b = static_cast(b->Next()) ) { if ( (b->Position().x >= map.Width()) || (b->Position().y >= map.Height()) ) { cerr << "Error: Shop (" << b->Position().x << '/' << b->Position().y << ") out of map" << endl; return 1; } if ( !(map.TerrainTypes( b->Position() ) & TT_ENTRANCE) ) { cerr << "Error: Map does not have a shop entrance at " << b->Position().x << '/' << b->Position().y << endl; return 1; } if ( b->NameID() == -1 ) { cerr << "Error: No name set for shop " << b->ID() << endl; return 1; } else if ( !GetMessage(b->NameID()) ) { cerr << "Error: Invalid name " << (short)b->NameID() << " for shop " << b->ID() << endl; return 1; } else { for ( std::map::const_iterator it = messages.GetLibrary().begin(); it != messages.GetLibrary().end(); ++it ) { const Language *lang = &it->second; if ( strlen(lang->GetMsg(b->NameID())) > 30 ) { cerr << "Error(" << lang->ID() << "): Shop names must not be longer than 30 characters" << endl; return 1; } } } if ( b->Crystals() > b->MaxCrystals() ) { cerr << "Error: Shop at " << b->Position().x << '/' << b->Position().y << " contains " << b->Crystals() << " crystals, but only " << b->MaxCrystals() << " fit in" << endl; return 1; } if ( b->MinWeight() > b->MaxWeight() ) { cerr << "Error: Shop at " << b->Position().x << '/' << b->Position().y << " has minweight (" << (short)b->MinWeight() << ") > maxweight (" << b->MaxWeight() << ')' << endl; return 1; } for ( walk = static_cast(b->Next()); walk; walk = static_cast(walk->Next()) ) { if ( b->ID() == walk->ID() ) { cerr << "Error: Two shops sharing ID " << b->ID() << endl; return 1; } if ( b->Position() == walk->Position() ) { cerr << "Error: Two shops sharing one hex (" << b->Position().x << '/' << b->Position().y << ')' << endl; return 1; } } for ( u = static_cast(units.Head()); u; u = static_cast(u->Next()) ) { if ( b->Position() == u->Position() ) { if ( (u->Owner() != b->Owner()) && (b->Owner() != PLAYER_NONE) ) { cerr << "Error: Hostile unit (" << u->ID() << ") in building (" << b->Position().x << '/' << b->Position().y << ')' << endl; return 1; } else u->SetOwner( b->Owner() ); if ( u->Weight() < b->MinWeight() ) { cerr << "Error: Unit (" << u->ID() << ") too light for building at " << b->Position().x << '/' << b->Position().y << endl; return 1; } else if ( u->Weight() > b->MaxWeight() ) { cerr << "Error: Unit (" << u->ID() << ") too heavy for building at " << b->Position().x << '/' << b->Position().y << endl; return 1; } u->SetFlags( U_SHELTERED ); } } } return 0; } /* check events for consistency */ int MissionParser::check_events( void ) const { Event *e; Building *b; Unit *u; short msg; for ( e = static_cast(events.Head()); e; e = static_cast(e->Next()) ){ if ( (e->Player() != PLAYER_ONE) && (e->Player() != PLAYER_TWO) ) { cerr << "Error: Event " << (short)e->ID() << " has no player assigned" << endl; return 1; } msg = e->Message(); if ( (msg != -1) && !messages.GetMsg(msg) ) { cerr << "Error: Event " << (short)e->ID() << " has invalid message '" << msg << "'" << endl; return 1; } msg = e->Title(); if ( (msg != -1) && !messages.GetMsg(msg) ) { cerr << "Error: Event " << (short)e->ID() << " has invalid title '" << msg << "'" << endl; return 1; } /* check dependencies */ if ( e->Dependency() != -1 ) { if ( !GetEventByID( e->Dependency() ) ) { cerr << "Error: Event " << (short)e->ID() << " depends on non-existing event " << (short)e->Dependency() << endl; return 1; } } if ( e->Discard() != -1 ) { if ( !GetEventByID( e->Discard() ) ) { cerr << "Error: Event " << (short)e->ID() << " discards non-existing event " << (short)e->Discard() << endl; return 1; } } /* check ID's */ if ( find_dups( events, e ) ) { cerr << "Error: Two or more events with the same ID '" << (short)e->ID() << "'" << endl; return 1; } switch ( e->Type() ) { case EVENT_CREATE_UNIT: if ( e->GetData(0) < 0 ) { cerr << "Error: No unit type specified for Create Unit event " << (short)e->ID() << endl; return 1; } if ( map.Index2Hex(e->GetData(1)) == Point(-1, -1) ) { cerr << "Error: Invalid location for event " << (short)e->ID() << endl; return 1; } { unsigned short cu_dir = e->GetData(2) & 0x0007; unsigned short cu_size = (e->GetData(2) & 0x0038) >> 3; unsigned short cu_xp = (e->GetData(2) & 0x01C0) >> 6; if ( cu_dir > NORTHWEST ) { cerr << "Error: Invalid direction " << cu_dir << " for event " << (short)e->ID() << endl; return 1; } if ( (cu_size == 0) || (cu_size > MAX_GROUP_SIZE) ) { cerr << "Error: Invalid unit size " << cu_size << " for event " << (short)e->ID() << endl; return 1; } if ( cu_xp > XP_MAX_LEVEL ) { cerr << "Error: Invalid unit experience level " << cu_xp << " for event " << (short)e->ID() << endl; return 1; } } break; case EVENT_DESTROY_UNIT: if ( e->GetData(0) < 0 ) { if (e->GetData(2) == -9999) { cerr << "Error: No target specified for Destroy Unit event " << (short)e->ID() << endl; return 1; } e->SetData(0, -1); } else { if ( !GetUnitByID( e->GetData(0) ) ) { cerr << "Error: Unit with ID " << e->GetData(0) << " specified in event " << (short)e->ID() << " does not exist" << endl; return 1; } e->SetData(2, 0); } if ( e->GetData(1) < -1 ) e->SetData(1, -1); else if ( e->GetData(1) > PLAYER_TWO ) { cerr << "Error: Invalid unit owner for event " << (short)e->ID() << endl; return 1; } break; case EVENT_MANIPULATE_EVENT: if ( e->GetData(0) < 0 ) { cerr << "Error: Event manipulation event " << (short)e->ID() << " without valid target" << endl; return 1; } /* for now, this event can only enable/disable other events */ e->SetData(1, EFLAG_DISABLED); if ( e->GetData(1) == 0 ) { cerr << "Error: Event manipulation (" << (short)e->ID() << ") with invalid flags 0" << endl; return 1; } if ( (e->GetData(2) < 0) || (e->GetData(2) > 2) ) { cerr << "Error: Event manipulation (" << (short)e->ID() << ") with invalid action " << e->GetData(2) << endl; return 1; } if ( !GetEventByID( e->GetData(0) ) ) { cerr << "Error: Event with ID " << e->GetData(0) << " does not exist" << endl; return 1; } break; case EVENT_MESSAGE: if ( e->Message() == -1 ) { cerr << "Error: Message event " << (short)e->ID() << " has no message" << endl; return 1; } if ( map.Index2Hex( e->GetData(0) ) == Point(-1,-1) ) e->SetData(0, -1); else if ( !map.Contains( map.Index2Hex( e->GetData(0) ) ) ) { cerr << "Error: Invalid location set for message event (" << (short)e->ID() << ')' << endl; return 1; } e->SetData(1, 0); e->SetData(2, 0); break; case EVENT_MINING: if ( e->GetData(0) < 0 ) { cerr << "Error: Mining event " << (short)e->ID() << " with no shop" << endl; return 1; } if ( (e->GetData(2) < 0) || (e->GetData(2) > 3) ) { cerr << "Error: Mining event " << (short)e->ID() << " with invalid action " << e->GetData(2) << endl; return 1; } if ( ((e->GetData(2) == 0) || (e->GetData(2) == 2)) && (e->GetData(1) < 0) ) { cerr << "Error: Trying to set negative absolute amount for mining event " << (short)e->ID() << endl; return 1; } if ( !GetBuildingByID(e->GetData(0)) ) { cerr << "Error: Shop with ID " << e->GetData(0) << " does not exist (event " << (short)e->ID() << ')' << endl; return 1; } break; case EVENT_CONFIGURE: if ( e->GetData(0) < -9999 ) { cerr << "Error: Configure event " << (short)e->ID() << " does not specify setting" << endl; return 1; } if ( (e->GetData(0) == 0) || (e->GetData(0) == 1) ) { msg = e->GetData(1); if ( (msg != -1) && !messages.GetMsg(msg) ) { cerr << "Error: Event " << (short)e->ID() << " references invalid message '" << msg << "'" << endl; return 1; } } e->SetData(2, 0); break; case EVENT_RESEARCH: if ( e->GetData(0) < 0 ) { cerr << "Error: Research event " << (short)e->ID() << " with no shop" << endl; return 1; } if ( e->GetData(2) == -9999 ) e->SetData(2, 0); else if ( (e->GetData(2) < 0) || (e->GetData(2) > 1) ) { cerr << "Error: Research event " << (short)e->ID() << " specifies invalid action " << e->GetData(2) << endl; return 1; } if ( e->GetData(1) < 0 ) { cerr << "Error: Research event " << (short)e->ID() << " with no unit type" << endl; return 1; } if ( !GetBuildingByID(e->GetData(0)) ) { cerr << "Error: Shop with ID " << e->GetData(0) << " does not exist (event " << (short)e->ID() << ')' << endl; return 1; } break; case EVENT_SCORE: if ( e->GetData(0) < 0 ) { cerr << "Warning: Corrected success rate for score event < 0" << endl; e->SetData(0, 0); } if ( e->GetData(1) < -1 ) e->SetData(1, -1); msg = e->GetData(1); if ( (msg != -1) && !messages.GetMsg(msg) ) { cerr << "Error: Event " << (short)e->ID() << " references invalid message '" << msg << "'" << endl; return 1; } if ( e->GetData(2) < -1 ) e->SetData(2, -1); msg = e->GetData(2); if ( (e->GetData(1) != -1) && (msg != -1) && !messages.GetMsg(msg) ) { cerr << "Error: Event " << (short)e->ID() << " references invalid message '" << msg << "'" << endl; return 1; } else if ( (e->GetData(1) == -1) && (msg != -1) ) { cerr << "Warning: Event " << (short)e->ID() << " set a title but no message" << endl; e->SetData(2, -1); } break; case EVENT_SET_HEX: if ( e->GetData(0) < 0 ) { cerr << "Error: No tile specified for Set Hex event " << (short)e->ID() << endl; return 1; } if ( map.Index2Hex( e->GetTData(1) ) == Point(-1,-1) ) e->SetTData(1, -1); else if ( !map.Contains( map.Index2Hex( e->GetTData(1) ) ) ) { cerr << "Error: Invalid location set for event trigger (" << (short)e->ID() << ')' << endl; return 1; } break; case EVENT_SET_TIMER: if ( e->GetData(0) < 0 ) { cerr << "Error: Set Timer event " << (short)e->ID() << " without valid target" << endl; return 1; } else { Event *tev = GetEventByID( e->GetData(0) ); if ( !tev ) { cerr << "Error: Event with ID " << e->GetData(0) << " does not exist (event " << (short)e->ID() << ")" << endl; return 1; } else if ( tev->Trigger() != ETRIGGER_TIMER ) { cerr << "Error: Event with ID " << e->GetData(0) << " has no timer trigger (event " << (short)e->ID() << ")" << endl; return 1; } } if ( e->GetData(1) < 0 ) { cerr << "Error: Set Timer (" << (short)e->ID() << ") with invalid time " << e->GetData(1) << endl; return 1; } if ( (e->GetData(2) < 0) || (e->GetData(2) > 2) ) { cerr << "Error: Set Timer (" << (short)e->ID() << ") with invalid offset " << e->GetData(2) << endl; return 1; } break; default: cerr << "Error: Event with ID " << (short)e->ID() << " has invalid type" << endl; } switch ( e->Trigger() ) { case ETRIGGER_TIMER: if ( e->GetTData(0) < 0 ) { cerr << "Error: Event trigger lacks time (" << (short)e->ID() << ')' << endl; return 1; } e->SetTData(1, 0); e->SetTData(2, 0); break; case ETRIGGER_UNIT_DESTROYED: if ( e->GetTData(0) >= 0 ) { u = GetUnitByID( e->GetTData(0) ); /* the event must also be triggered when the unit is not destroyed but captured by the enemy. Therefore we need the original owner */ if (u) e->SetTData(1, u->Owner()); else { cerr << "Error: Event trigger targets non-existing unit with ID " << e->GetTData(0) << endl; return 1; } } else { if ( -e->GetTData(0) - 2 >= unit_set->NumTiles() ) { cerr << "Error: Event trigger targets non-existing unit type " << -e->GetTData(0) - 2 << " (" << (short)e->ID() << ')' << endl; return 1; } if ( e->GetTData(1) == -9999 ) e->SetTData(1, e->Player()^1); } e->SetTData(2, 0); break; case ETRIGGER_HAVE_UNIT: if ( (e->GetTData(1) != PLAYER_ONE) && (e->GetTData(1) != PLAYER_TWO) ) { cerr << "Error: Event trigger wants invalid player to own a unit (" << (short)e->ID() << endl; return 1; } if ( e->GetTData(2) < 0 ) e->SetTData(2, -1); u = GetUnitByID( e->GetTData(0) ); if (u) { if ( (u->Owner() == e->GetTData(1)) && (e->GetTData(2) < 0) && (e->Dependency() == -1) ) { cerr << "Error: Event trigger: unit " << u->ID() << " is already owned by player " << u->Owner()+1 << '(' << (short)e->ID() << ')' << endl; return 1; } } else { cerr << "Error: Event trigger targets non-existing unit " << e->GetTData(0) << " (" << (short)e->ID() << ')' << endl; return 1; } break; case ETRIGGER_HAVE_BUILDING: if ( (e->GetTData(1) != PLAYER_ONE) && (e->GetTData(1) != PLAYER_TWO) ) { cerr << "Error: Event trigger wants invalid player to own a shop (" << (short)e->ID() << endl; return 1; } if ( e->GetTData(2) < 0 ) e->SetTData(2, -1); b = GetBuildingByID( e->GetTData(0) ); if (b) { if ( (b->Owner() == e->GetTData(1)) && (e->GetTData(2) < 0) && (e->Dependency() == -1) ) { cerr << "Error: Event trigger: shop " << b->ID() << " is already owned by player " << b->Owner()+1 << '(' << (short)e->ID() << ')' << endl; return 1; } } else { cerr << "Error: Event trigger targets non-existing shop " << e->GetTData(0) << " (" << (short)e->ID() << ')' << endl; return 1; } break; case ETRIGGER_HAVE_CRYSTALS: if ( e->GetTData(0) == -9999 ) { cerr << "Error: Crystals trigger does not specify amount (" << (short)e->ID() << ")" << endl; return 1; } else if ( ABS(e->GetTData(0)) > 5000 ) { cerr << "Error: Invalid crystals amount " << e->GetTData(0) << " for event trigger (" << (short)e->ID() << ")" << endl; return 1; } if ( e->GetTData(1) == -9999 ) e->SetTData(1, e->Player()); else if ( (e->GetTData(1) != PLAYER_ONE) && (e->GetTData(1) != PLAYER_TWO) ) { cerr << "Error: Event trigger wants invalid player to own a shop (" << (short)e->ID() << ")" << endl; return 1; } if ( e->GetTData(2) < -2 ) e->SetTData(2, -1); else if ( e->GetTData(2) >= 0 ) { if ( !GetBuildingByID( e->GetTData(2) ) ) { cerr << "Error: Event trigger targets non-existing shop " << e->GetTData(2) << " (" << (short)e->ID() << ')' << endl; return 1; } } break; case ETRIGGER_UNIT_POSITION: if ( map.Index2Hex( e->GetTData(1) ) == Point(-1,-1) ) { cerr << "Error: Invalid location set for event trigger (" << (short)e->ID() << ')' << endl; return 1; } if ( e->GetTData(0) < -1 ) { if ( -e->GetTData(0) - 2 >= unit_set->NumTiles() ) { cerr << "Error: Event trigger targets non-existing unit type " << -e->GetTData(0) - 2 << " (" << (short)e->ID() << ')' << endl; return 1; } } else if ( e->GetTData(0) >= 0 ) { if (!GetUnitByID( e->GetTData(0) )) { cerr << "Error: Event trigger targets non-existing unit " << e->GetTData(0) << " (" << (short)e->ID() << ')' << endl; return 1; } } if ( e->GetTData(2) == -9999 ) e->SetTData( 2, e->Player() ); else if ( (e->GetTData(2) != PLAYER_ONE) && (e->GetTData(2) != PLAYER_TWO) ) { cerr << "Error: Event trigger wants invalid player to control a unit (" << (short)e->ID() << endl; return 1; } break; case ETRIGGER_HANDICAP: if ( e->GetTData(0) == -9999 ) { cerr << "Error: Handicap event trigger without a handicap (" << (short)e->ID() << ')' << endl; return 1; } else if ( e->GetTData(0) & 0xFFF8 ) { cerr << "Error: Invalid handicap " << e->GetTData(0) << " (" << (short)e->ID() << ')' << endl; return 1; } e->SetTData(1, 0); e->SetTData(2, 0); break; default: cerr << "Error: Invalid event trigger type " << (short)e->Trigger() << " (" << (short)e->ID() << ')' << endl; return 1; } } return 0; } /* load a unit set */ int MissionParser::load_unit_set( const char *set ) { string setshort( set ); // keep only the file part; check for both Unix and Windows path // separator characters or this breaks when building with MinGW setshort = setshort.substr( setshort.find_last_of( "/\\" ) + 1 ); size_t pos = setshort.find( ".units" ); if ( pos != string::npos ) setshort.erase( pos ); File file( set ); if ( !file.Open("rb") ) return -1; unit_set = new UnitSet(); if ( unit_set->Load( file, setshort.c_str() ) == -1 ) { delete unit_set; unit_set = 0; return -1; } map.SetUnitSet( unit_set ); return 0; } /* load a terrain set */ int MissionParser::load_tile_set( const char *set ) { string setshort( set ); // keep only the file part; check for both Unix and Windows path // separator characters or this breaks when building with MinGW setshort = setshort.substr( setshort.find_last_of( "/\\" ) + 1 ); size_t pos = setshort.find( ".tiles" ); if ( pos != string::npos ) setshort.erase( pos ); File file( set ); if ( !file.Open("rb") ) return -1; terrain_set = new TerrainSet(); if ( terrain_set->Load( file, setshort.c_str() ) == -1 ) { delete terrain_set; return -1; } map.SetTerrainSet( terrain_set ); return 0; } MissionParser::MissionParser( void ) { SetLevelInfoMsg( -1 ); SetCampaignName( -1 ); SetCampaignInfo( -1 ); SetTitle( "Unknown" ); } crimson-0.5.3/tools/bi2cf.h0000644000076400001440000000511610274443225012416 00000000000000/* bi2cf - A map converter for Crimson Fields copyright (c) 2002 by Florian Dietrich 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 (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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ //----------------------------------------------------------------------------- // bi2cf.h: // header file for bi2cf, some data types etc. // // not much to say about this... //----------------------------------------------------------------------------- #define MAP_NONE 0 #define MAP_BI 1 #define MAP_BIDD1 2 #define MAP_BIDD2 3 #define MAP_HL 4 // this will hold the properties of a building typedef struct { unsigned char owner; // owner of buildung/unit [0|1|2] unsigned char type; // type [0=HQ|1=Factory|2=Depot|3=Unit] unsigned char index; // xth unit/building of type unsigned char crystals; // number of crystals inside unsigned char unknown; // probably max capacity of crystals? unsigned char units[7]; // units inside building/unit } tshpdata; // this will hold all the game-specific conversion data typedef struct { int type; const unsigned char (*units)[20]; const unsigned short *rawtiles; const unsigned char *buildings; const unsigned char *transport; int maxunits; } tconvdata; // this will hold terrain and unit information of a field typedef struct { unsigned char terrain; unsigned char unit; } tfindata; // this will hold all of the map data read from the files typedef struct { unsigned char *rawfin; tfindata *map; int mapx, mapy, mapsize, finsize; unsigned char *rawshp; unsigned char *canbuild; tshpdata *buildings; int numbuildings, shpsize; } tmapdata; // this will hold all the filenames and directories typedef struct { char *inputdir; char *outputdir; char *srcfilename; char *finfilename; char *shpfilename; } tmapfiles; // this will hold all the map information given by the user typedef struct { int players; char *number; char *title; char *info; char *tileset; char *unitset; } tmapinfo; crimson-0.5.3/tools/mklocale.cpp0000644000076400001440000001516710554366261013567 00000000000000/* mklocale -- compile or disassemble a language file for Crimson Fields Copyright (C) 2004-2007 Jens Granseuer 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 (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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include "SDL.h" #include "lang.h" #include "globals.h" #ifdef _MSC_VER // SDL_Main linkage destroys the command line in VS8 #undef main #endif class LanguageTemplate { public: int Parse(const char *fname, Language &lang) const; int Write(const char *fname, const Language &lang) const; private: int ParseMessages(ifstream &file, Language &lang) const; void RemWS(string &str) const; }; static char parse_args(int argc, char *argv[]); static const char *templatefile; static const char *localefile; /* an exemplary language template file might look like this: * * # foobar language file for Crimson Fields * # translated by Foo Bar * language=foobar * id=fo * * [messages] * first message * # <- message delimiter * second message * # multiple delimiters are possible * # * third message * containing a line break * [/messages] */ int LanguageTemplate::Parse(const char *fname, Language &lang) const { int rc = 0; ifstream file(fname); if (file.is_open()) { string buf, value; unsigned int line = 0; size_t i; while (!file.eof()) { getline(file, buf); ++line; RemWS(buf); if ((buf.size() > 0) && (buf[0] != '#')) { /* ignore comments and empty lines */ if (strncasecmp(buf.c_str(), "language", 8) == 0) { i = buf.find("=", 8); if (i == string::npos) { cerr << "Error in line " << line << ": format invalid" << endl; rc = -1; break; } value = buf.substr(i+1, 50); RemWS(value); lang.SetName(value.c_str()); } else if (strncasecmp(buf.c_str(), "id", 2) == 0) { i = buf.find("=", 2); if (i == string::npos) { cerr << "Error in line " << line << ": format invalid" << endl; rc = -1; break; } value = buf.substr(i+1, 10); RemWS(value); if (value.size() != 2) { cerr << "Error in line " << line << ": only two-character identifiers allowed" << endl; rc = -1; break; } lang.SetID(value.c_str()); } else if (strncasecmp(buf.c_str(), "[messages]", 10) == 0) { rc = ParseMessages(file, lang); } else { rc = -1; cerr << "Error in line " << line << ": unkown token" << endl; break; } } } file.close(); if (strlen(lang.ID()) == 0) { cerr << "Error parsing template: no language id found" << endl; rc = -1; } else if (strlen(lang.Name()) == 0) { cerr << "Error parsing template: no language name found" << endl; rc = -1; } } else cerr << "Error opening template file" << endl; return rc; } int LanguageTemplate::ParseMessages(ifstream &file, Language &lang) const { bool done = false; string buf, msg; do { getline(file, buf); RemWS(buf); if ((buf.size() > 0) && ((buf[0] == '#') || (strncasecmp(buf.c_str(), "[/messages]", 11) == 0))) { /* save last message */ if (!msg.empty()) { lang.AddMsg(msg); msg.erase(); } if (strncasecmp(buf.c_str(), "[/messages]", 11) == 0) done = true; } else { if (!msg.empty()) msg += '\n'; msg.append(buf); } } while ( !file.eof() && !done ); int rc = 0; if (file.eof()) { rc = -1; cerr << "Error: messages section unterminated" << endl; } return rc; } /* remove leading and trailing spaces and cr + lf from the string */ void LanguageTemplate::RemWS(string &str) const { size_t i = 0; if (str.empty()) return; while (str[i] == ' ') ++i; str.erase(0, i); for (i = str.size() - 1; (i >= 0) && ((str[i] == ' ') || (str[i] == '\n') || (str[i] == '\r')); --i); /* empty loop */ str.erase(i+1); } int LanguageTemplate::Write(const char *fname, const Language &lang) const { int rc; ofstream file(fname); if (file.is_open()) { file << "# " << lang.Name() << " language template for Crimson Fields " << VERSION << endl; file << "language=" << lang.Name() << endl; file << "id=" << lang.ID() << endl; file << endl; file << "[messages]" << endl; const char *msg = lang.GetMsg(0); file << msg << endl; for (int i = 1; (msg = lang.GetMsg(i)) != 0; ++i) { file << '#' << endl; file << msg << endl; } file << "[/messages]" << endl; file.close(); rc = 0; } else { rc = -1; cerr << "Error opening template file" << endl; } return rc; } int main(int argc, char *argv[]) { if (SDL_Init(0) < 0) { cerr << "Couldn't init SDL:" << endl << SDL_GetError(); exit(-1); } atexit(SDL_Quit); char op = parse_args(argc, argv); if (op == 0) return 1; Language lang; LanguageTemplate tmpl; int rc; if (op == 'l') { rc = tmpl.Parse(templatefile, lang); if (rc == 0) { rc = lang.WriteCatalog(localefile); if (rc == -1) cerr << "Error writing catalog file" << endl; else rc = 0; } } else { rc = lang.ReadCatalog(localefile); if (rc != -1) { rc = tmpl.Write(templatefile, lang); } else cerr << "Error reading catalog file" << endl; } return rc; } char parse_args(int argc, char *argv[]) { bool usage = true; char rc = 0; if (argc == 4) { string op(argv[1]); if (op == "-l") { rc = 'l'; templatefile = argv[2]; localefile = argv[3]; usage = false; } else if (op == "-t") { rc = 't'; templatefile = argv[3]; localefile = argv[2]; usage = false; } } if (usage) { cerr << "Usage:" << endl << argv[0] << " -l