1 module d2dmap
is aliced
;
11 // ////////////////////////////////////////////////////////////////////////// //
12 public final class LevelMap
{
14 private import std
.stdio
: File
;
16 enum MapVersion
= 2; // óÁÍÁÑ ÐÏÓÌÅÄÎÑÑ ×ÅÒÓÉÑ ËÁÒÔÙ
17 public enum MapSize
= 100;
24 TILE_DOORC
= 2, // closed door
25 TILE_DOORO
= 3, // opened door
30 TILE_MBLOCK
= 8, // just blocks monsters
46 MB_MUSIC
, // usually 8 bytes
47 MB_SKY
, // ushort: [1..3]
80 static struct MapThing
{
84 DeathMatch
= 0x0010, // ÐÏÑ×ÌÑÅÔÓÑ ÔÏÌØËÏ × DeathMatch'Å
87 short x
, y
; // ËÏÏÒÄÉÎÁÔÙ
89 ushort flags
; // ÆÌÁÇÉ
91 @property const pure nothrow @safe @nogc {
92 bool left () => ((flags
&DirRight
) == 0);
93 bool right () => ((flags
&DirRight
) != 0);
94 bool dmonly () => ((flags
&DeathMatch
) != 0);
98 static struct MapSwitch
{
99 ubyte x
, y
; // ËÏÏÒÄÉÎÁÔÙ/8
101 ubyte tm
; // ÄÏÌÖÎÏ ÂÙÔØ 0
102 ubyte a
, b
; // ÏÂÙÞÎÏ - ËÏÏÒÄÉÎÁÔÙ/8 Ä×ÅÒÉ
103 ushort c
; // ÎÅ ÉÓÐÏÌØÚÕÅÔÓÑ (×ÒÏÄÅ ÂÙ)
104 ubyte flags
; // ÆÌÁÇÉ (SW_*)
108 enum { Type
, Front
, Back
, Water
, Lava
, Acid
, LightMask
} // tile types, Front+: megatexture types
109 ubyte[][3] tiles
; // Type, Front, Back
110 string
[] wallnames
; // "_water_0": water, "_water_1": acid, "_water_2": lava
115 MapSwitch
[] switches
;
117 this (string fname
) { load(fname
); }
118 this (File fl
) { load(fl
); }
129 void dump (int idx
) {
130 static char to62 (ubyte b
) { pragma(inline
, true); return cast(char)(b
< 10 ?
'0'+b
: (b
-10 < 26 ?
'A'+(b
-10) : 'a'+(b
-10-26))); }
131 foreach (immutable y
; 0..MapSize
) {
132 foreach (immutable x
; 0..MapSize
) {
133 conwrite(to62(tiles
[idx
][y
*MapSize
+x
]));
140 bool getThingPos (ushort id
, int* x
=null, int* y
=null, ushort* flags
=null) {
141 foreach (ref th
; things
[]) {
143 if (x
!is null) *x
= th
.x
;
144 if (y
!is null) *y
= th
.y
;
145 if (flags
!is null) *flags
= th
.flags
;
149 if (x
!is null) *x
= 0;
150 if (y
!is null) *y
= 0;
151 if (flags
!is null) *flags
= 0;
156 void calcMapSize () {
157 bool isEmpty(string dir
) (int x
, int y
) if (dir
== "col" || dir
== "row") {
158 while (x
< MapSize
&& y
< MapSize
) {
159 if (tiles
[0][y
*MapSize
+x
] || tiles
[1][y
*MapSize
+x
] || tiles
[2][y
*MapSize
+x
]) return false;
160 static if (dir
== "row") ++x
; else ++y
;
164 width
= height
= MapSize
;
166 while (width
> 0 && isEmpty
!"col"(width
-1, 0)) --width
;
168 while (height
> 0 && isEmpty
!"row"(0, height
-1)) --height
;
171 void load (string fname
) {
172 import std
.stdio
: File
;
173 load(openFile(fname
));
176 void load(ST
) (auto ref ST st
) if (isReadableStream
!ST
) {
178 scope(failure
) clear
;
181 st
.rawReadExact(sign
[]);
182 if (sign
!= "Doom2D\x1a\x00") throw new Exception("invalid map signature");
183 if (st
.readNum
!ushort() != MapVersion
) throw new Exception("invalid map version");
186 foreach (ref a
; tiles
[]) a
= new ubyte[](MapSize
*MapSize
);
187 char[$] skyname
= "sprites/sky/rsky1.vga";
189 auto btype
= st
.readNum
!ushort();
190 if (btype
== MB_END
) break; // no more blocks
191 auto bsubtype
= st
.readNum
!ushort();
192 auto bsize
= st
.readNum
!uint();
193 if (bsize
== 0) continue; // skip this block, it has no data (wtf?!)
194 // various tile types
197 if (bsize
!= 2) throw new Exception("invalid sky data size");
198 ushort num
= st
.readNum
!ushort();
199 if (num
>= 1 && num
<= 3) skyname
[$-5] = cast(char)('0'+num
);
204 if (bsubtype
> 1) throw new Exception("unknown tile block subtype");
205 int idx
= (btype
== MB_BACK ? Back
: (btype
== MB_FRONT ? Front
: Type
));
206 //ubyte[MapSize*MapSize] data = 0;
207 auto data
= tiles
[idx
];
209 if (bsize
!= data
.length
) throw new Exception("invalid tile data size");
210 st
.rawReadExact(data
[]);
213 auto pkdata
= new ubyte[](bsize
);
214 st
.rawReadExact(pkdata
[]);
215 int spos
= 0, opos
= 0;
216 while (spos
< pkdata
.length
) {
217 ubyte b
= pkdata
[spos
++];
221 int count
= pkdata
[spos
++];
222 count |
= pkdata
[spos
++]<<8;
224 while (count
-- > 0) data
[opos
++] = b
;
227 assert(opos
== data
.length
);
229 // copy unpacked data
230 //foreach (immutable y; 0..MapSize) tiles[idx][y*MapSize] = data[y*MapSize..(y+1)*MapSize];
233 wallnames
.length
= 0;
235 //wallnames[] = null;
236 while (bsize
>= 8+1) {
238 st
.rawReadExact(texname
[]);
239 auto type
= st
.readNum
!ubyte();
242 foreach (char ch
; texname
) {
244 if (ch
>= 'A' && ch
<= 'Z') ch
+= 32;
247 //tn = texname[0..idx+1];
249 import std
.uni
: toLower
;
250 wallnames
~= koi8lotranslit(tns
).toLower
;
253 if (bsize
!= 0) throw new Exception("invalid texture chunk size");
254 debug { conwriteln(wallnames
.length
, " textures loaded"); }
260 t
.x
= st
.readNum
!short();
261 t
.y
= st
.readNum
!short();
262 t
.type
= st
.readNum
!ushort();
263 t
.flags
= st
.readNum
!ushort();
264 if (t
.type
!= 0) things
~= t
;
266 if (bsize
!= 0) throw new Exception("invalid thing chunk size");
272 sw
.x
= st
.readNum
!ubyte();
273 sw
.y
= st
.readNum
!ubyte();
274 sw
.type
= st
.readNum
!ubyte();
275 sw
.tm
= st
.readNum
!ubyte();
276 sw
.a
= st
.readNum
!ubyte();
277 sw
.b
= st
.readNum
!ubyte();
278 sw
.c
= st
.readNum
!ushort();
279 sw
.flags
= st
.readNum
!ubyte();
282 if (bsize
!= 0) throw new Exception("invalid thing chunk size");
285 auto pkdata
= new ubyte[](bsize
);
286 st
.rawReadExact(pkdata
[]);
291 skytex
= skyname
.idup
;
292 debug { conwriteln(width
, "x", height
); }