/* Copyright (C) 1996-1997 Aleksey Volynskov * Copyright (C) 2011 Rambo * Copyright (C) 2020 SovietPony * * 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, version 3 of the License ONLY. * * 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, see . */ #include "map.h" #include "game.h" #include "items.h" #include "things.h" #include "monster.h" #include "switch.h" #include "view.h" #include "music.h" #include "render.h" #include #include #include "error.h" #include "common/streams.h" #include "common/files.h" #include "common/cp866.h" enum { MB_COMMENT = -1, MB_END = 0, MB_WALLNAMES, MB_BACK, MB_WTYPE, MB_FRONT, MB_THING, MB_SWITCH, MB_MUSIC, MB_SKY, MB_SWITCH2, MB__UNKNOWN }; typedef struct map_header_t { char id[8]; short ver; } map_header_t; typedef struct map_block_t { short t; short st; int sz; } map_block_t; typedef struct old_thing_t { short x, y; short t; unsigned short f; } old_thing_t; static map_block_t blk; static int G_load (Stream *h) { switch (blk.t) { case MB_MUSIC: stream_read(g_music, 8, 1, h); //if (music_random) { // F_randmus(g_music); //} MUS_load(g_music); return 1; } return 0; } static int IT_load (Stream *h) { int m, i, j; old_thing_t t; switch (blk.t) { case MB_THING: for (i = 0; blk.sz > 0; ++i, blk.sz -= 8) { t.x = stream_read16(h); t.y = stream_read16(h); t.t = stream_read16(h); t.f = stream_read16(h); it[i].o.x = t.x; it[i].o.y = t.y; it[i].t = t.t; it[i].s = t.f; if (it[i].t && (it[i].s & THF_DM) && !g_dm) { it[i].t=0; } } m = i; for (i = 0, j = -1; i < m; ++i) { if (it[i].t == TH_PLR1) { j = i; it[i].t = 0; } } if (!g_dm) { if (j == -1) { logo("Player 1 point not exists on the map\n"); return 0; // error } dm_pos[0].x = it[j].o.x; dm_pos[0].y = it[j].o.y; dm_pos[0].d = it[j].s & THF_DIR; } for (i = 0, j = -1; i < m; ++i) { if (it[i].t == TH_PLR2) { j = i; it[i].t = 0; } } if (!g_dm && _2pl) { if (j == -1) { logo("Player 2 point not exists on the map\n"); return 0; // error } dm_pos[1].x = it[j].o.x; dm_pos[1].y = it[j].o.y; dm_pos[1].d = it[j].s & THF_DIR; } for (i = 0, j = 0; i < m; ++i) { if (it[i].t == TH_DMSTART) { if (g_dm) { dm_pos[j].x = it[i].o.x; dm_pos[j].y = it[i].o.y; dm_pos[j].d = it[i].s & THF_DIR; } it[i].t = 0; ++j; } } if (g_dm && j < 2) { logo("Required at least two DM points on the map\n"); return 0; // error } if (g_dm) { dm_pnum = j; dm_pl1p = myrand(dm_pnum); do { dm_pl2p = myrand(dm_pnum); } while (dm_pl2p == dm_pl1p); } else { dm_pl1p = 0; dm_pl2p = 1; dm_pnum = 2; } PL_spawn(&pl1, dm_pos[dm_pl1p].x, dm_pos[dm_pl1p].y, dm_pos[dm_pl1p].d); if (_2pl) { PL_spawn(&pl2, dm_pos[dm_pl2p].x, dm_pos[dm_pl2p].y, dm_pos[dm_pl2p].d); } for (i = 0; i < m; ++i) { if (it[i].t >= TH_CLIP && it[i].t < TH_DEMON) { it[i].s = 0; it[i].t = it[i].t - TH_CLIP + I_CLIP; if (it[i].t >= I_KEYR && it[i].t <= I_KEYB) { it[i].t |= 0x8000; } } else if (it[i].t >= TH_DEMON) { MN_spawn(it[i].o.x, it[i].o.y, it[i].s & THF_DIR, it[i].t - TH_DEMON + MN_DEMON); it[i].t = 0; } } return 1; } return 0; } static int SW_load (Stream *h) { int i; switch(blk.t) { case MB_SWITCH2: sw_secrets = 0; for (i = 0; i < MAXSW && blk.sz > 0; ++i, blk.sz -= 9) { sw[i].x = stream_read8(h); sw[i].y = stream_read8(h); sw[i].t = stream_read8(h); sw[i].tm = stream_read8(h); // unused sw[i].a = stream_read8(h); sw[i].b = stream_read8(h); sw[i].c = stream_read8(h); sw[i].d = stream_read8(h); // unused sw[i].f = stream_read8(h); sw[i].tm = 0; sw[i].d = 0; sw[i].f |= 0x80; if (sw[i].t == SW_SECRET) { ++sw_secrets; } } return 1; } return 0; } static void unpack (void *buf, int len, void *obuf) { int i = 0; int j = 0; unsigned char *p = buf; unsigned char *q = obuf; while (i < len) { int id = p[i]; int step = 1; i += 1; if (id == 0xff) { step = p[i] | p[i + 1] << 8; id = p[i + 2]; i += 3; } memset(&q[j], id, step); j += step; } } static int read_array (void *p, Stream *h) { void *buf; switch (blk.st) { case 0: stream_read(p, FLDW * FLDH, 1, h); break; case 1: buf = malloc(blk.sz); if (buf == NULL) { logo("Out of memory\n"); return 0; // error } stream_read(buf, blk.sz, 1, h); unpack(buf, blk.sz, p); free(buf); break; default: return 0; } return 1; } static int W_load (Stream *h) { int i; char s[8]; switch (blk.t) { case MB_WALLNAMES: R_begin_load(); memset(walf, 0, sizeof(walf)); for (i = 1; i < 256 && blk.sz > 0; i++, blk.sz -= 9) { stream_read(s, 8, 1, h); walf[i] = stream_read8(h) ? 1 : 0; // ??? R_load(s); if (cp866_strncasecmp(s, "VTRAP01", 8) == 0) { walf[i] |= 2; } } R_end_load(); return 1; case MB_BACK: return read_array(fldb, h); case MB_WTYPE: return read_array(fld, h); case MB_FRONT: return read_array(fldf, h); case MB_SKY: sky_type = stream_read16(h); R_loadsky(sky_type); return 1; } return 0; } int MAP_load (Stream *r) { int ok = 0; long off; map_header_t hdr; assert(r != NULL); W_init(); // reset all game data stream_read(hdr.id, 8, 1, r); hdr.ver = stream_read16(r); if (memcmp(hdr.id, "Doom2D\x1A", 8) == 0) { ok = 1; while (ok) { blk.t = stream_read16(r); blk.st = stream_read16(r); blk.sz = stream_read32(r); off = stream_getpos(r) + blk.sz; switch (blk.t) { case MB_MUSIC: ok = G_load(r); break; case MB_WALLNAMES: case MB_BACK: case MB_WTYPE: case MB_FRONT: case MB_SKY: ok = W_load(r); break; case MB_THING: ok = IT_load(r); break; case MB_SWITCH2: ok = SW_load(r); break; case MB_COMMENT: /* skip */ break; case MB_END: return ok; default: logo("Unknown block %d(%d)\n", blk.t, blk.st); return 0; // error } stream_setpos(r, off); } } else { logo("Invalid map header\n"); abort(); ok = 0; } return ok; }