initial work for switch actors
[dd2d.git] / mapexport / exmap.d
blob8e7f279edec02b0982998dbba9a34cfba5f136fc
1 module exmap is aliced;
3 private:
4 import console;
5 import wadarc;
7 import iv.stream;
8 import iv.strex;
10 import d2dmap;
11 import d2dadefs;
14 // ////////////////////////////////////////////////////////////////////////// //
15 import std.stdio : File;
17 void exportMapScript (string modname, string mapname, LevelMap map, File fo) {
19 void writeRects (const(ubyte)[] tilemap, string layer) {
20 auto tiles = tilemap.dup;
22 ubyte getAt (int x, int y) {
23 pragma(inline, true);
24 return (x >= 0 && y >= 0 && x < map.width && y < map.height ? tiles[y*map.width+x] : 0);
27 void setAt (int x, int y, ubyte v) {
28 pragma(inline, true);
29 if (x >= 0 && y >= 0 && x < map.width && y < map.height) tiles[y*map.width+x] = v;
32 bool isLineFilledWith(string mode) (int x, int y, int len, ubyte tnum) {
33 static if (mode == "horizontal") {
34 enum dx = 1;
35 enum dy = 0;
36 } else if (mode == "vertical") {
37 enum dx = 0;
38 enum dy = 1;
39 } else {
40 static assert(0, "wtf?!");
42 assert(len > 0);
43 while (len-- > 0) {
44 if (getAt(x, y) != tnum) return false;
45 x += dx;
46 y += dy;
48 return true;
51 bool isRectFilledWith (int x, int y, int w, int h, ubyte tnum) {
52 assert(w > 0);
53 assert(h > 0);
54 while (h-- > 0) {
55 if (!isLineFilledWith!"horizontal"(x, y, w, tnum)) return false;
56 ++y;
58 return true;
61 void fillRectWith (int x, int y, int w, int h, ubyte tnum) {
62 assert(w > 0);
63 assert(h > 0);
64 while (h-- > 0) {
65 foreach (int sx; x..x+w) setAt(sx, y, tnum);
66 ++y;
70 foreach (int y; 0..map.height) {
71 foreach (int x; 0..map.width) {
72 ubyte tnum = getAt(x, y);
73 if (tnum == 0) continue;
74 if (tnum >= map.wallnames.length) {
75 import std.string : format;
76 throw new Exception("tile type %s exceeds max texture number (%s)".format(tnum, map.wallnames.length-1));
78 // stupid brute force
79 int bestw = 1, besth = 1;
80 foreach (int ey; y..map.height) {
81 foreach (int ex; x..map.width) {
82 int w = ex-x+1;
83 int h = ey-y+1;
84 if (isRectFilledWith(x, y, w, h, tnum)) {
85 if (w*h > bestw*besth) { bestw = w; besth = h; }
89 fillRectWith(x, y, bestw, besth, 0);
90 if (bestw == 1 && besth == 1) {
91 fo.writeln(" mapSetTile(", map.wallnames[tnum].quote, ", ", layer, ", ", x, ", ", y, ");");
92 } else {
93 fo.writeln(" mapSetRect(", map.wallnames[tnum].quote, ", ", layer, ", ", x, ", ", y, ", ", bestw, ", ", besth, ");");
99 void writeTypeRects (const(ubyte)[] tilemap) {
100 auto tiles = tilemap.dup;
102 ubyte getAt (int x, int y) {
103 pragma(inline, true);
104 return (x >= 0 && y >= 0 && x < map.width && y < map.height ? tiles[y*map.width+x] : 0);
107 void setAt (int x, int y, ubyte v) {
108 pragma(inline, true);
109 if (x >= 0 && y >= 0 && x < map.width && y < map.height) tiles[y*map.width+x] = v;
112 bool isLineFilledWith(string mode) (int x, int y, int len, ubyte tnum) {
113 static if (mode == "horizontal") {
114 enum dx = 1;
115 enum dy = 0;
116 } else if (mode == "vertical") {
117 enum dx = 0;
118 enum dy = 1;
119 } else {
120 static assert(0, "wtf?!");
122 assert(len > 0);
123 while (len-- > 0) {
124 if (getAt(x, y) != tnum) return false;
125 x += dx;
126 y += dy;
128 return true;
131 bool isRectFilledWith (int x, int y, int w, int h, ubyte tnum) {
132 assert(w > 0);
133 assert(h > 0);
134 while (h-- > 0) {
135 if (!isLineFilledWith!"horizontal"(x, y, w, tnum)) return false;
136 ++y;
138 return true;
141 void fillRectWith (int x, int y, int w, int h, ubyte tnum) {
142 assert(w > 0);
143 assert(h > 0);
144 while (h-- > 0) {
145 foreach (int sx; x..x+w) setAt(sx, y, tnum);
146 ++y;
150 foreach (int y; 0..map.height) {
151 foreach (int x; 0..map.width) {
152 ubyte tnum = getAt(x, y);
153 if (tnum == 0) continue;
154 string tname;
155 switch (tnum) {
156 case LevelMap.TILE_WALL: tname = "TILE_WALL"; break;
157 case LevelMap.TILE_DOORC: tname = "TILE_DOORC"; break;
158 case LevelMap.TILE_DOORO: tname = "TILE_DOORO"; break;
159 case LevelMap.TILE_STEP: tname = "TILE_STEP"; break;
160 case LevelMap.TILE_WATER: tname = "TILE_WATER"; break;
161 case LevelMap.TILE_ACID1: tname = "TILE_ACID1"; break;
162 case LevelMap.TILE_ACID2: tname = "TILE_ACID2"; break;
163 case LevelMap.TILE_MBLOCK: tname = "TILE_MBLOCK"; break;
164 case LevelMap.TILE_LIFTU: tname = "TILE_LIFTU"; break;
165 case LevelMap.TILE_LIFTD: tname = "TILE_LIFTD"; break;
166 case LevelMap.TILE_ACTTRAP: tname = "TILE_ACTTRAP"; break;
167 default:
168 import std.string : format;
169 throw new Exception("unknown tile type: %s".format(tnum));
171 // stupid brute force
172 int bestw = 1, besth = 1;
173 foreach (int ey; y..map.height) {
174 foreach (int ex; x..map.width) {
175 int w = ex-x+1;
176 int h = ey-y+1;
177 if (isRectFilledWith(x, y, w, h, tnum)) {
178 if (w*h > bestw*besth) { bestw = w; besth = h; }
182 fillRectWith(x, y, bestw, besth, 0);
183 if (bestw == 1 && besth == 1) {
184 fo.writeln(" mapSetTypeTile(", tname, ", ", x, ", ", y, ");");
185 } else {
186 fo.writeln(" mapSetTypeRect(", tname, ", ", x, ", ", y, ", ", bestw, ", ", besth, ");");
192 void writeThings (string ctype) {
193 foreach (ref thing; map.things) {
194 //if (thing.dmonly) continue;
195 if (auto did = thing.type in d2dactordefsById) {
196 if (did.classtype != ctype) continue;
197 if (thing.dmonly) fo.write(" if (gameMode == GM_DEATHMATCH) "); else fo.write(" ");
198 fo.writeln("actorSpawn(", did.classtype.quote, ", ", did.classname.quote, ", ", cast(int)thing.x, ", ", cast(int)thing.y, ", ACTOR_DIR_", (thing.right ? "RIGHT" : "LEFT"), ");");
199 } else {
200 conwriteln("ignoring unknown D2D thing with mapid ", thing.type);
201 continue;
206 void writeSwitches () {
207 foreach (ref sw; map.switches) {
208 if (sw.type == 0) continue; // SW_NONE
209 assert(sw.tm == 0);
210 assert(sw.c == 0);
211 fo.write(" actorSpawnVanillaSwitch(");
212 switch (sw.type) {
213 case LevelMap.SW_NONE: assert(0);
214 case LevelMap.SW_EXIT: fo.write("exit".quote); break;
215 case LevelMap.SW_EXITS: fo.write("exitSecret".quote); break;
216 case LevelMap.SW_OPENDOOR: fo.write("doorOpen".quote); break;
217 case LevelMap.SW_SHUTDOOR: fo.write("doorClose".quote); break;
218 case LevelMap.SW_SHUTTRAP: fo.write("trapClose".quote); break;
219 case LevelMap.SW_DOOR: fo.write("door".quote); break;
220 case LevelMap.SW_DOOR5: fo.write("doorTimed90".quote); break;
221 case LevelMap.SW_PRESS: fo.write("press".quote); break;
222 case LevelMap.SW_TELE: fo.write("teleport".quote); break;
223 case LevelMap.SW_SECRET: fo.write("secret".quote); break;
224 case LevelMap.SW_LIFTUP: fo.write("liftUp".quote); break;
225 case LevelMap.SW_LIFTDOWN: fo.write("liftDown".quote); break;
226 case LevelMap.SW_TRAP: fo.write("trapClose".quote); break;
227 case LevelMap.SW_LIFT: fo.write("liftToggle".quote); break;
228 case LevelMap.SW_WINGAME: fo.write("exitWin".quote); break;
229 default: assert(0, "invalid switch type");
231 fo.write(", ", sw.x*8, ", ", sw.y*8, ", ");
232 // flags
233 fo.write("0");
234 if (sw.flags&LevelMap.SW_PL_PRESS) fo.write("|SWITCH_PLAYER_PRESS");
235 if (sw.flags&LevelMap.SW_MN_PRESS) fo.write("|SWITCH_MONSTER_PRESS");
236 if (sw.flags&LevelMap.SW_PL_NEAR) fo.write("|SWITCH_PLAYER_PROXIMITY");
237 if (sw.flags&LevelMap.SW_MN_NEAR) fo.write("|SWITCH_MONSTER_PROXIMITY");
238 if (sw.flags&LevelMap.SW_KEY_R) fo.write("|SWITCH_KEY_RED");
239 if (sw.flags&LevelMap.SW_KEY_G) fo.write("|SWITCH_KEY_GREEN");
240 if (sw.flags&LevelMap.SW_KEY_B) fo.write("|SWITCH_KEY_BLUE");
241 fo.writeln(", ", sw.a, ", ", sw.b, ");");
245 fo.writeln("module ", modname, ";");
246 fo.writeln();
247 fo.writeln("import actor, mapapi, stdlib;");
248 fo.writeln();
249 fo.writeln();
250 fo.writeln("public void buildMap () {");
251 fo.writeln(" mapSetSize(", map.width, ", ", map.height, ");");
252 fo.writeln(" mapSetName(", mapname.quote, ");");
253 fo.writeln(" putTiles();");
254 fo.writeln(" putItems();");
255 fo.writeln(" putMonsters();");
256 fo.writeln(" putSwitches();");
257 fo.writeln("}");
259 fo.writeln();
260 fo.writeln();
261 fo.writeln("void putItems () {");
262 writeThings("item");
263 fo.writeln("}");
265 fo.writeln();
266 fo.writeln();
267 fo.writeln("void putMonsters () {");
268 writeThings("monster");
269 fo.writeln("}");
271 fo.writeln();
272 fo.writeln();
273 fo.writeln("void putSwitches () {");
274 writeSwitches();
275 fo.writeln("}");
277 fo.writeln();
278 fo.writeln();
279 fo.writeln("void putTiles () {");
280 fo.writeln(" // back");
281 writeRects(map.tiles[LevelMap.Back], "LAYER_BACK");
282 fo.writeln(" // front");
283 writeRects(map.tiles[LevelMap.Front], "LAYER_FRONT");
284 fo.writeln(" // types");
285 writeTypeRects(map.tiles[LevelMap.Type]);
286 fo.writeln("}");
290 // ////////////////////////////////////////////////////////////////////////// //
291 void main (string[] args) {
292 string[] wadList;
293 string mapName = null;
295 for (usize idx = 1; idx < args.length; ++idx) {
296 if (args[idx] == "--map" || args[idx] == "-map") {
297 import std.algorithm : remove;
298 if (mapName !is null) assert(0, "duplicate '--map'");
299 mapName = args[idx+1];
300 args = args.remove(idx, 2);
301 --idx; //hack
302 } else {
303 wadList ~= args[idx];
307 void setDP () {
308 version(rdmd) {
309 setDataPath("data");
310 } else {
311 import std.file : thisExePath;
312 import std.path : dirName;
313 setDataPath(thisExePath.dirName);
315 addWad("/home/ketmar/k8prj/doom2d-tl/data/doom2d.wad");
316 foreach (string s; wadList) addWad(s);
319 setDP();
321 if (mapName is null) mapName = "map01";
323 conwriteln("loading '", mapName, "'...");
324 auto map = new LevelMap("maps/"~mapName~".d2m");
326 conwriteln("writing '", mapName, ".dacs'...");
327 exportMapScript(mapName, mapName, map, File(mapName~".dacs", "w"));