UFO: Alien Invasion
Loading...
Searching...
No Matches
sv_rma.cpp
Go to the documentation of this file.
1
7
8/*
9All original material Copyright (C) 2002-2025 UFO: Alien Invasion.
10
11Original file from Quake 2 v3.21: quake2-2.31/server/sv_init.c
12
13Copyright (C) 1997-2001 Id Software, Inc.
14
15This program is free software; you can redistribute it and/or
16modify it under the terms of the GNU General Public License
17as published by the Free Software Foundation; either version 2
18of the License, or (at your option) any later version.
19
20This program is distributed in the hope that it will be useful,
21but WITHOUT ANY WARRANTY; without even the implied warranty of
22MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23
24See the GNU General Public License for more details.
25
26You should have received a copy of the GNU General Public License
27along with this program; if not, write to the Free Software
28Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29
30*/
31
32#include "server.h"
33#include "sv_rma.h"
34#include "../shared/parse.h"
35#include "../shared/thread.h"
36
37#define ASSEMBLE_THREADS 2
39#define PRINT_RMA_PROGRESS 0
41#define SORT_BY_SIZE 1
42
44#define RMA2_MAX_REC 64
46/* How to calculate this value:
47 * Take he largest map of the project, eg. +farm
48 * farm large has a size of 12x12.
49 * If we had only 'fits everywhere'-tiles of size 1x1,
50 * we'd need 12x12=144 x tiletypes(25) = 3600 (in theory).
51 * Larger tiles eg. 2x2 would reduce the positions from 144 to 11x11=121
52 * Required tiles with exactly one instance ie. "1 1" reduce both the # of tiletypes
53 * and the # of positions by their size.
54 * The neighbouring requirements of required tiles will further reduce the value.
55 * => It's best to determine a working value empirically.
56 */
57#define RMA2_MAX_TILEPOS 1700
59#define TCM 50
62
64#define GAPS 25
65#define MAX_RANDOM_MAP_WIDTH 32
66#define MAX_RANDOM_MAP_HEIGHT 32
69
71static SDL_sem* mapSem;
72static SDL_cond* mapCond;
73static SDL_mutex* mapLock;
74static Uint32 threadID;
75
76#define MAX_MAPASSEMBLIES 32
79#define MAX_TILETYPES 128
80#define MAX_TILESETS 16
81#define MAX_TILESETTILES 16
82#define MAX_TILESIZE 16
83#define MAX_FIXEDTILES 64
84
86class Tile {
87public:
88 char id[MAX_VAR];
89 unsigned long spec[MAX_TILESIZE][MAX_TILESIZE];
90 int w, h;
91 int area;
92};
93
95class TileSet {
96public:
97 char id[MAX_VAR];
100};
101
102#define MAX_ASSEMBLY_SEEDS 32
103
132
137typedef struct mToPlace_s {
139 int min, max;
140 int cnt;
141} mToPlace_t;
142
147typedef struct mPlaced_s {
148 const Tile* tile;
149 int x, y;
150 int idx, pos;
151} mPlaced_t;
152
154{
156public:
159
163
166
167 mPlaced_t mPlaced[MAX_MAPTILES];
169
172
175 unsigned long lineFlags;
176
179
180 int asmIdx;
181
183
184 inline const Assembly* getCurrentAssembly () const {
185 return &assemblies[asmIdx];
186 }
187 inline void setName (const char* mapTheme) {
188 Q_strncpyz(this->name, mapTheme, sizeof(this->name));
189 }
190 inline const char* getName () const {
191 return name;
192 }
193 inline const char* getCurrentAssemblyTitle () const {
194 return assemblies[asmIdx].title;
195 }
196};
197
203static void RandomList (const int n, short* list)
204{
205 short i;
206
207 for (i = 0; i < n; i++)
208 list[i] = i;
209
210 for (i = 0; i < n; i++) {
211 const short r = rand() % (i + (n - i));
212 const short t = list[r];
213 list[r] = list[i];
214 list[i] = t;
215 }
216}
217
218#define ALL_TILES (0xfffffffeUL)
219#define IS_SOLID(x) ((x)&1UL)
220
245static unsigned long tileMask (const char chr)
246{
247 if (chr == '+')
248 return 1UL;
249 else if (chr == '0')
250 return ALL_TILES;
251 else if (chr >= '1' && chr <= '5')
252 return 1UL << (chr - '0');
253 else if (chr >= 'a' && chr <= 'z')
254 return 1UL << (chr - 'a' + 6);
255 else if (chr >= 'A' && chr <= 'Z')
256 return 1UL << (chr - 'A' + 6);
257
258 Com_Error(ERR_DROP, "SV_ParseMapTile: Invalid tile char '%c'", chr);
259}
260
261static void SV_TileMaskToString (unsigned long m, char* str)
262{
263 int i;
264 int j = 0; /* writepos */
265
266 if (m == ALL_TILES) {
267 str[0] = '0';
268 str[1] = 0;
269 return;
270 }
271 if (m & 1) {
272 str[0] = '+';
273 j = 1;
274 }
275 for (i = 1; i < 6; i++) {
276 if (m >> i & 1) {
277 str[j] = '0' + i;
278 j++;
279 }
280 }
281 for (i = 6; i < 32; i++) {
282 if (m >> i & 1) {
283 str[j] = 'a' - 6 + i;
284 j++;
285 }
286 }
287 str[j] = 0;
288}
289
290#define ACW 6 /* ascii cell width */
291#define ACH 3 /* ascii cell height */
292#define MMW 13 /* map max width 13 means we support 12 */
293#define MMH 13 /* map max height */
294static void SV_RmaPrintMap (const MapInfo* map)
295{
296 const Assembly* mAsm = map->getCurrentAssembly();
297 char screen[(MMH + 1) * ACH][(MMW + 1) * ACW];
298 int i, j;
299
300 assert(mAsm->height < MMH);
301 assert(mAsm->width < MMW);
302
303 /* initialize */
304 int rb = (1 + mAsm->width) * ACW; /* index of right border */
305 OBJSET(screen, ' ');
306 for (i = 0; i < MMH * ACH + 1; i++) {
307 screen[i][rb + 1] = '|';
308 screen[i][rb + 2] = 0;
309 }
310
311 /* fill in the data */
312 for (i = 0; i < map->numPlaced; i++) {
313 const mPlaced_t* mp = &map->mPlaced[i];
314 const Tile* tile = mp->tile;
315 int tx, ty;
316 const char* tn = tile->id + 1;
317
318 if (!strncmp(tn, "craft_", 6))
319 tn += 6;
320 for (ty = 0; ty < tile->h; ty++) {
321 for (tx = 0; tx < tile->w; tx++) {
322 if (IS_SOLID(tile->spec[ty][tx])) {
323 int cbX = ACW * (mp->x + tx);
324 int cbY = ACH * (mp->y + ty);
325 char flags[33] = {0,};
326
327 /* write the tilename */
328 for (j = 0; j < ACW - 1; j++) {
329 if (tn[j])
330 screen[cbY + ACH - 1][cbX + 1 + j] = tn[j];
331 else
332 break;
333 }
334
335 /* get the properties of that solid */
336 SV_TileMaskToString(tile->spec[ty][tx], flags);
337 /* write the flags */
338 for (j = 0; j < ACW - 1; j++) {
339 if (flags[j])
340 screen[cbY + ACH - 2][cbX + 1 + j] = flags[j];
341 else
342 break;
343 }
344
345
346 /* left border of tile */
347 if (tx > 0 && !IS_SOLID(tile->spec[ty][tx - 1])) {
348 for (j = 0; j < ACH; j++)
349 screen[cbY + j][cbX] = '!';
350 }
351 if (!IS_SOLID(tile->spec[ty][tx + 1])) {
352 for (j = 0; j < ACH; j++)
353 screen[cbY + j][cbX + ACW] = '!';
354 }
355 if (ty > 0 && !IS_SOLID(tile->spec[ty - 1][tx])) {
356 for (j = 1; j < ACW; j++)
357 screen[cbY][cbX + j] = '-';
358 }
359 if (!IS_SOLID(tile->spec[ty + 1][tx])) {
360 for (j = 1; j < ACW; j++)
361 screen[cbY + ACH][cbX + j] = '-';
362 }
363 }
364 }
365 }
366 }
367
368 /* now add the specs of the gaps */
369 int cx, cy;
370 int height = mAsm->height;
371 int width = mAsm->width;
372 for (cy = 0; cy <= height; cy++) {
373 for (cx = 0; cx <= width; cx++) {
374 if (!IS_SOLID(map->curMap[cy][cx])) {
375 const int cbX = ACW * (cx);
376 const int cbY = ACH * (cy);
377 char flags2[33] = {0,};
378
379 /* get the requirements of that gap */
380 SV_TileMaskToString(map->curMap[cy][cx], flags2);
381 /* write the flags */
382 for (j = 0; j < ACW - 1; j++) {
383 if (flags2[j])
384 screen[cbY + ACH - 2][cbX + 1 + j] = flags2[j];
385 else
386 break;
387 }
388 }
389 }
390 }
391
392 /* print it */
393 const char* underscores = "_________________________________________________________________________\n";
394 Com_Printf("\nCurrent state of the map:\n");
395 int w = ACW * (MMW - 1 - mAsm->width);
396 Com_Printf("%s", underscores + w);
397 int h = ACH * (height + 1);
398 for (i = h; i >= ACH; i--)
399 Com_Printf("%s\n", screen[i] + ACW);
400 Com_Printf("%s", underscores + w);
401}
402
403static const TileSet* SV_GetMapTileSet (const MapInfo* map, const char* tileSetName)
404{
405 for (int i = 0; i < map->numTileSets; i++) {
406 const TileSet* tileSet = &map->tileSets[i];
407 if (Q_streq(tileSetName, tileSet->id))
408 return tileSet;
409 }
410
411 return nullptr;
412}
413
414static inline const Tile* SV_GetMapTile (const MapInfo* map, const char* tileName)
415{
416 for (int i = 0; i < map->numTiles; i++) {
417 const Tile* tile = &map->mTile[i];
418 if (Q_streq(tileName, tile->id))
419 return tile;
420 }
421
422 return nullptr;
423}
424
429static bool SV_ParseMapTileSet (const char* filename, const char** text, MapInfo* map, bool inherit)
430{
431 const char* errhead = "SV_ParseMapTileSet: Unexpected end of file (";
432 const char* token;
433 TileSet* target = &map->tileSets[map->numTileSets];
434
435 OBJZERO(*target);
436
437 /* get tileset name */
438 token = Com_EParse(text, errhead, filename);
439 if (!*text)
440 return false;
441
442 Q_strncpyz(target->id, token, sizeof(target->id));
443
444 /* start parsing the block */
445 token = Com_EParse(text, errhead, filename);
446 if (!*text)
447 return false;
448 if (*token != '{') {
449 Com_Printf("SV_ParseMapTileSet: Expected '{' for tileset '%s' (%s)\n", target->id, filename);
450 return false;
451 }
452
453 do {
454 token = Com_EParse(text, errhead, filename);
455 if (!*text)
456 return false;
457 if (token[0] != '}') {
458 if (target->numTiles >= MAX_TILESETTILES)
459 Com_Error(ERR_DROP, "Max tileset limit reached for tileset '%s'", target->id);
460 else { /* just to get rid of the 'mixed decl and code' warning */
461 char* tileTarget = target->tiles[target->numTiles];
462 const size_t size = sizeof(target->tiles[target->numTiles]);
463 if (inherit) {
464 if (token[0] == '+')
465 token++;
466
467 Com_sprintf(tileTarget, size, "%s%s", map->inheritBasePath, token);
468 } else {
469 Q_strncpyz(tileTarget, token, size);
470 }
471
472 if (SV_GetMapTile(map, tileTarget) != nullptr)
473 target->numTiles++;
474 else
475 Com_Error(ERR_DROP, "Did not find tile '%s' from tileset '%s'", tileTarget, target->id);
476 }
477 }
478 } while (token[0] != '}');
479
480 map->numTileSets++;
481 return false;
482}
483
488static bool SV_ParseMapTile (const char* filename, const char** text, MapInfo* map, bool inherit)
489{
490 const char* errhead = "SV_ParseMapTile: Unexpected end of file (";
491 Tile* target = &map->mTile[map->numTiles];
492 target->area = 0;
493
494 /* get tile name */
495 const char* token = Com_EParse(text, errhead, filename);
496 if (!*text)
497 return false;
498
499 OBJZERO(*target);
500
501 if (inherit) {
502 if (token[0] == '+')
503 token++;
504 Com_sprintf(target->id, sizeof(target->id), "%s%s", map->inheritBasePath, token);
505 } else {
506 Q_strncpyz(target->id, token, sizeof(target->id));
507 }
508
509 /* start parsing the block */
510 token = Com_EParse(text, errhead, filename);
511 if (!*text)
512 return false;
513 if (*token != '{') {
514 Com_Printf("SV_ParseMapTile: Expected '{' for tile '%s' (%s)\n", target->id, filename);
515 return false;
516 }
517
518 /* get width and height */
519 token = Com_EParse(text, errhead, filename);
520 if (!*text)
521 return false;
522 target->w = atoi(token);
523
524 token = Com_EParse(text, errhead, filename);
525 if (!*text)
526 return false;
527 target->h = atoi(token);
528
529 if (target->w > MAX_TILESIZE || target->h > MAX_TILESIZE) {
530 Com_Printf("SV_ParseMapTile: Bad tile size [%i %i] (%s) (max. [%i %i])\n", target->w, target->h, filename, MAX_TILESIZE, MAX_TILESIZE);
531 *text = strchr(*text, '}');
532 return false;
533 }
534
535 /* get tile specs */
536 for (int y = target->h - 1; y >= 0; y--)
537 for (int x = 0; x < target->w; x++) {
538 token = Com_EParse(text, errhead, filename);
539 if (!*text || *token == '}') {
540 Com_Printf("SV_ParseMapTile: Bad tile desc in '%s' - not enough entries for size\n", target->id);
541 *text = strchr(*text, '}') + 1;
542 return 0;
543 }
544 target->spec[y][x] = 0UL;
545 for (int i = 0; token[i]; i++) {
546 target->spec[y][x] |= tileMask(token[i]);
547 }
548 if (IS_SOLID(target->spec[y][x]))
549 target->area++; /* # of solid tiles */
550 }
551
552 token = Com_EParse(text, errhead, filename);
553
554 /* get connections */
555 if (*token != '}')
556 Com_Printf("SV_ParseMapTile: Bad tile desc in '%s' - too many entries for size\n", target->id);
557
558 /* successfully parsed - this tile counts */
559 return true;
560}
561
572static const char* SV_GetCvarToken (const MapInfo* map, const Assembly* a, const char* token, const char* filename, const char** text, const char* errhead)
573{
574 const cvar_t* cvar;
575
576 Com_DPrintf(DEBUG_SERVER, "SV_GetCvarToken: cvar replacement: %s\n", token);
577
578 cvar = Cvar_FindVar(token);
579
580 token = Com_EParse(text, errhead, filename);
581 if (!text || token[0] == '}')
582 return nullptr;
583
584 if (cvar == nullptr)
585 return token;
586
587 Com_DPrintf(DEBUG_SERVER, "SV_ParseAssembly: cvar replacement value: %s\n", cvar->string);
588 if (cvar->string[0] != '+') {
589 Com_Printf("SV_ParseAssembly: warning - cvar '%s' value doesn't seem to be a valid tile id '%s' - set to default '%s'\n",
590 cvar->name, cvar->string, token);
591 Cvar_Set(cvar->name, "%s", token);
592 if (token[0] != '+' && !strchr(token, '/'))
593 Com_Error(ERR_DROP, "SV_ParseAssembly: wrong tile id in assembly '%s'", a->id);
594
595 return token;
596 }
597
598 /*
599 * Allow cvar replacement to use inherited tiles - see FR #3446
600 * @todo a better way to do this?
601 */
602 const char* tokenTile = strrchr(token, '/');
603 if (tokenTile) {
604 const char* cvarTile = cvar->string + 1;
605 for (int i = 0; i < map->numTiles; i++) {
606 const char* tileId = map->mTile[i].id;
607 const char* tileName = strrchr(tileId, '/');
608 if (tileName && strstr(tileName, cvarTile) && !Q_strncasecmp(tileId, token, tokenTile - token))
609 return tileId;
610 }
611 }
612
613 return cvar->string;
614}
615
616static const char* SV_GetTileFromTileSet (const MapInfo* map, const char* filename, const char** text, const Assembly* a)
617{
618 const char* errhead = "SV_GetTileFromTileSet: Unexpected end of file (";
619 const TileSet* tileSet;
620 int random;
621 const char* token;
622
623 /* get tileset id */
624 token = Com_EParse(text, errhead, filename);
625 if (!text)
626 Com_Error(ERR_DROP, "SV_GetTileFromTileSet: illegal tileset syntax in assembly '%s' in %s", a->id, filename);
627
628 tileSet = SV_GetMapTileSet(map, token);
629 if (tileSet == nullptr)
630 Com_Error(ERR_DROP, "SV_GetTileFromTileSet: Could not find tileset %s in %s (assembly %s)", token, filename, a->id);
631
632 random = rand() % tileSet->numTiles;
633 return tileSet->tiles[random];
634}
635
644static bool SV_ParseAssemblySeeds (MapInfo* map, const char* filename, const char** text, Assembly* a)
645{
646 const char* errhead = "SV_ParseAssemblySeeds: Unexpected end of file (";
647 const char* token;
648
649 /* start parsing the block */
650 token = Com_EParse(text, errhead, filename);
651 if (!*text)
652 return false;
653 if (*token != '{') {
654 Com_Printf("SV_ParseAssemblySeeds: Expected '{' for seed of assembly '%s' (%s)\n", a->id, filename);
655 return false;
656 }
657
658 for (;;) {
659 token = Com_EParse(text, errhead, filename);
660 if (!*text || token[0] == '}')
661 break;
662
663 if (a->numSeeds < lengthof(a->seeds)) {
664 a->seeds[a->numSeeds++] = atoi(token);
665 } else {
666 Com_Printf("too many seeds for %s (%s) - ignore seed %s\n", a->id, filename, token);
667 }
668 }
669 return true;
670}
671
672static void SV_GetTilesFromTileSet (const MapInfo* map, const char* filename, const char** text, Assembly* a)
673{
674 const char* errhead = "SV_GetTilesFromTileSet: Unexpected end of file (";
675 const TileSet* tileSet;
676 int min, max;
677 const char* token;
678
679 /* get tileset id */
680 token = Com_EParse(text, errhead, filename);
681 if (!text)
682 Com_Error(ERR_DROP, "SV_GetTilesFromTilesSet: illegal tileset syntax in assembly '%s' in %s", a->id, filename);
683 tileSet = SV_GetMapTileSet(map, token);
684 if (tileSet == nullptr)
685 Com_Error(ERR_DROP, "SV_GetTilesFromTilesSet: Could not find tileset %s in %s (assembly %s)", token, filename, a->id);
686
687 /* get min and max tileset number */
688 token = Com_EParse(text, errhead, filename);
689 if (!text || *token == '}')
690 Com_Error(ERR_DROP, "SV_GetTilesFromTilesSet: Error in assembly %s (invalid syntax for tileset %s)", filename, tileSet->id);
691 if (!strstr(token, " "))
692 Com_Error(ERR_DROP, "SV_GetTilesFromTilesSet: Error in assembly %s (min max value of tileset %s)", filename, tileSet->id);
693 sscanf(token, "%i %i", &min, &max);
694 if (min > max)
695 Com_Error(ERR_DROP, "SV_GetTilesFromTilesSet: Error in assembly %s (min is bigger than max for tileset %s)", filename, tileSet->id);
696 if (max <= 0)
697 Com_Error(ERR_DROP, "SV_GetTilesFromTilesSet: Error in assembly %s (max is <= 0 for tileset %s)", filename, tileSet->id);
698 /* set min and max tile numbers (increasing random tiles until the required number is reached) */
699 for (int i = max, j = min; i > 0; --i) {
700 const int random = rand() % tileSet->numTiles;
701 const Tile* tile = SV_GetMapTile(map, tileSet->tiles[random]);
702 if (tile != nullptr) {
703 const ptrdiff_t tileIdx = tile - map->mTile;
704 ++a->max[tileIdx];
705 if (j > 0) {
706 ++a->min[tileIdx];
707 --j;
708 }
709 } else {
710 Com_Error(ERR_DROP, "Could not find tile: '%s' in tileset '%s' (%s)", tileSet->tiles[random], tileSet->id, filename);
711 }
712 }
713}
714
727static bool SV_ParseAssembly (MapInfo* map, const char* filename, const char** text, Assembly* a)
728{
729 const char* errhead = "SV_ParseAssembly: Unexpected end of file (";
730 const char* token;
731 int x, y;
732 const Tile* tile;
733
734 /* get assembly name */
735 token = Com_EParse(text, errhead, filename);
736 if (!*text)
737 return false;
738
739 /* init */
740 OBJZERO(*a);
741 Q_strncpyz(a->id, token, sizeof(a->id));
742 a->width = 8;
743 a->height = 8;
744 a->dx = 1;
745 a->dy = 1;
746
747 token = Com_EParse(text, errhead, filename);
748 if (!*text || *token != '{')
749 Com_Error(ERR_DROP, "Invalid assembly definition '%s' - invalid token '%s' (%s)", a->id, token, filename);
750
751 do {
752 /* get tile name */
753 token = Com_EParse(text, errhead, filename);
754 if (!text || *token == '}')
755 break;
756
757 if (Q_streq(token, "title")) {
758 /* get map title */
759 token = Com_EParse(text, errhead, filename);
760 if (!text)
761 break;
762
763 Q_strncpyz(a->title, token, sizeof(a->title));
764 continue;
765 } else if (Q_streq(token, "multiplayer")) {
766 /* get map title */
767 token = Com_EParse(text, errhead, filename);
768 if (!text)
769 break;
770
771 /* a multiplayer only tile - forced to be exactly once in the map when
772 * we are playing a multiplayer match */
773 if (sv_maxclients->integer > 1) {
774 const Tile* t = SV_GetMapTile(map, token);
775 if (t != nullptr) {
776 const ptrdiff_t i = t - map->mTile;
777 a->min[i] = 1;
778 a->max[i] = 1;
779 } else {
780 Com_Error(ERR_DROP, "Could not find multiplayer tile: '%s' in assembly '%s' (%s)", token, a->id, filename);
781 }
782 }
783 continue;
784 } else if (Q_streq(token, "size")) {
785 /* get map size */
786 token = Com_EParse(text, errhead, filename);
787 if (!text)
788 break;
789
790 sscanf(token, "%i %i", &a->width, &a->height);
791 a->size = a->width * a->height;
792 continue;
793 } else if (Q_streq(token, "seeds")) {
794 if (!SV_ParseAssemblySeeds(map, filename, text, a))
795 return false;
796 continue;
797 } else if (Q_streq(token, "grid")) {
798 /* get map size */
799 token = Com_EParse(text, errhead, filename);
800 if (!text)
801 break;
802
803 sscanf(token, "%i %i", &a->dx, &a->dy);
804 continue;
805 /* chose a tile from a tileset */
806 } else if (Q_streq(token, "tileset")) {
807 SV_GetTilesFromTileSet(map, filename, text, a);
808 continue;
809 /* fix tilename "x y" */
810 } else if (Q_streq(token, "fix")) {
811 const Tile* t;
812
813 /* get tile */
814 token = Com_EParse(text, errhead, filename);
815 if (!text)
816 break;
817
818 if (token[0] == '*') {
819 token = SV_GetCvarToken(map, a, token + 1, filename, text, errhead);
820 if (token == nullptr)
821 break;
822 } else if (Q_streq(token, "tileset")) {
823 token = SV_GetTileFromTileSet(map, filename, text, a);
824 }
825
826 t = SV_GetMapTile(map, token);
827 if (t != nullptr) {
828 const ptrdiff_t i = t - map->mTile;
829 if (a->numFixed >= MAX_FIXEDTILES)
830 Com_Error(ERR_DROP, "SV_ParseAssembly: Too many fixed tiles in assembly '%s' (%s)", a->id, filename);
831
832 /* get coordinates */
833 token = Com_EParse(text, errhead, filename);
834 if (!text)
835 Com_Error(ERR_DROP, "SV_ParseAssembly: Error in assembly %s - could not get coordinates for fixed tile", filename);
836
837 sscanf(token, "%i %i", &x, &y);
838 if (x < 0 || x >= MAX_RANDOM_MAP_WIDTH) {
839 Com_Error(ERR_DROP, "SV_ParseAssembly: Error, invalid fixed coordinates given for x (%i) boundaries are: [0:%i] (%s).",
841 } else if (y < 0 || y >= MAX_RANDOM_MAP_HEIGHT) {
842 Com_Error(ERR_DROP, "SV_ParseAssembly: Error, invalid fixed coordinates given for y (%i) - boundaries are: [0:%i] (%s).",
844 }
845 a->fX[a->numFixed] = x;
846 a->fY[a->numFixed] = y;
847 a->fT[a->numFixed] = i;
848 a->numFixed++;
849 } else
850 Com_Error(ERR_DROP, "Could not find fixed tile: '%s' in assembly '%s' (%s)", token, a->id, filename);
851 continue;
852 /* <format>*cvarname <defaultvalue> "min max"</format> */
853 } else if (token[0] == '*') {
854 token = SV_GetCvarToken(map, a, token + 1, filename, text, errhead);
855 if (token == nullptr)
856 break;
857 }
858
859 tile = SV_GetMapTile(map, token);
860 if (tile != nullptr) {
861 const ptrdiff_t i = tile - map->mTile;
862 /* get min and max tile number */
863 token = Com_EParse(text, errhead, filename);
864 if (!text || *token == '}')
865 Com_Error(ERR_DROP, "SV_ParseAssembly: Error in assembly %s (invalid syntax for tile %s)", filename, tile->id);
866
867 if (!strstr(token, " "))
868 Com_Error(ERR_DROP, "SV_ParseAssembly: Error in assembly %s (min max value of tile %s)", filename, tile->id);
869
870 sscanf(token, "%i %i", &x, &y);
871 a->min[i] = x;
872 a->max[i] = y;
873 if (a->min[i] > a->max[i])
874 Com_Error(ERR_DROP, "SV_ParseAssembly: Error in assembly %s (min is bigger than max for tile %s)", filename, tile->id);
875 if (a->max[i] <= 0)
876 Com_Error(ERR_DROP, "SV_ParseAssembly: Error in assembly %s (max is <= 0 for tile %s)", filename, tile->id);
877 } else {
878 Com_Error(ERR_DROP, "Could not find tile: '%s' in assembly '%s' (%s)", token, a->id, filename);
879 }
880 } while (text);
881
882 return true;
883}
884
885
892static void SV_CombineAlternatives (unsigned long* mapAlts, const unsigned long tileAlts)
893{
894 /* don't touch solid fields of the map, return if tile has no connection info */
895 if (IS_SOLID(*mapAlts) || (tileAlts == ALL_TILES))
896 return;
897
898 /* copy if tile is solid */
899 if (IS_SOLID(tileAlts)) {
900 *mapAlts = tileAlts;
901 /* combine otherways */
902 } else {
903 *mapAlts &= tileAlts;
904 }
905}
906
910static void SV_ClearMap (MapInfo* map)
911{
912 unsigned long* mp = &map->curMap[0][0];
913 const unsigned long* end = &map->curMap[MAX_RANDOM_MAP_HEIGHT - 1][MAX_RANDOM_MAP_WIDTH - 1];
914
915 while (mp <= end)
916 *(mp++) = ALL_TILES;
917}
918
926static bool SV_FitTile (const MapInfo* map, const Tile* tile, const int x, const int y)
927{
928 int tx, ty;
929 const unsigned long* spec = nullptr;
930 const unsigned long* m = nullptr;
931 const Assembly* mAsm = map->getCurrentAssembly();
932
933 /* check for valid grid positions */
934 assert(x % mAsm->dx == 0);
935 assert(y % mAsm->dy == 0);
936 assert(tile);
937
938 if (x < 0 || y < 0)
939 return false;
940
941 /* check for map border */
942 if (x + tile->w > mAsm->width + 2 || y + tile->h > mAsm->height + 2)
943 return false;
944 unsigned long combined;
945#if 0
946 /* shortcut: most tiles are solid at [1][1], so check this first */
947 spec = &tile->spec[1][1];
948 m = &map->curMap[y+1][x+1];
949 combined = (*m) & (*spec);
950 if (IS_SOLID(combined) || !combined)
951 return false;
952#endif
953 /* test for fit */
954 spec = &tile->spec[0][0];
955 m = &map->curMap[y][x];
956 for (ty = 0; ty < tile->h; ty++) {
957 for (tx = 0; tx < tile->w; tx++, spec++, m++) {
958 combined = (*m) & (*spec);
959
960 /* quit if both are solid or no equal connection is found */
961 if (IS_SOLID(combined) || !combined)
962 return false;
963 }
964 spec += (MAX_TILESIZE - tile->w);
965 m += (MAX_RANDOM_MAP_WIDTH - tile->w);
966 }
967
968 return true;
969}
970
976static bool SV_TestFilled (const MapInfo* map)
977{
978 int x, y;
979 const Assembly* mAsm = map->getCurrentAssembly();
980
981 for (y = 1; y < mAsm->height + 1; y++)
982 for (x = 1; x < mAsm->width + 1; x++)
983 if (!IS_SOLID(map->curMap[y][x]))
984 return false;
985
986 return true;
987}
988
992static void SV_DumpPlaced (const MapInfo* map, int pl)
993{
994 int x, y;
995 const Assembly* mAsm = map->getCurrentAssembly();
996 const int h = mAsm->height;
997 const int w = mAsm->width;
998 const mPlaced_t* placed = &map->mPlaced[pl];
999
1000 Com_Printf("Placed tile %s at %d %d\n", placed->tile->id, placed->x, placed->y);
1001
1002 for (y = h; y >= 1; y--) {
1003 for (x = 1; x < w + 1; x++) {
1004 const int dx = x - placed->x;
1005 const int dy = y - placed->y;
1006
1007 if (dx >= 0 && dx < placed->tile->w && dy >= 0 && dy < placed->tile->h &&
1008 IS_SOLID(placed->tile->spec[dy][dx]))
1009 Com_Printf(" X");
1010 else
1011 Com_Printf(" .");
1012 }
1013 Com_Printf("\n");
1014 }
1015 Com_Printf("\n");
1016}
1017
1028static void SV_AddTile (MapInfo* map, const Tile* tile, int x, int y, int idx, int pos)
1029{
1030 int tx, ty;
1031#ifdef DEBUG
1032 const Assembly* mAsm = map->getCurrentAssembly();
1033
1034 /* check vor valid grid positions */
1035 assert(x % mAsm->dx == 0);
1036 assert(y % mAsm->dy == 0);
1037#endif
1038
1039 /* add the new tile */
1040 for (ty = 0; ty < tile->h; ty++)
1041 for (tx = 0; tx < tile->w; tx++) {
1042 assert(y + ty < MAX_RANDOM_MAP_HEIGHT);
1043 assert(x + tx < MAX_RANDOM_MAP_WIDTH);
1044
1045 SV_CombineAlternatives(&map->curMap[y + ty][x + tx], tile->spec[ty][tx]);
1046 }
1047
1048 /* add the tile to the array of placed tiles*/
1049 if (map->numPlaced >= MAX_MAPTILES)
1050 Com_Error(ERR_DROP, "SV_AddTile: Too many map tiles");
1051
1052 map->mPlaced[map->numPlaced].tile = tile;
1053 map->mPlaced[map->numPlaced].x = x;
1054 map->mPlaced[map->numPlaced].y = y;
1055 map->mPlaced[map->numPlaced].idx = idx;
1056 map->mPlaced[map->numPlaced].pos = pos;
1057
1058 map->numPlaced++;
1059
1060 if (idx >= 0) {
1061 map->mToPlace[idx].cnt++;
1062 }
1063}
1064
1073static void SV_RemoveTile (MapInfo* map, int* idx, int* pos)
1074{
1075 int tx, ty;
1076 int i, index;
1077
1078 SV_ClearMap(map);
1079
1080 if (map->numPlaced == 0)
1081 return;
1082
1083 map->numPlaced--;
1084 index = map->mPlaced[map->numPlaced].idx;
1085
1086 if (index >= 0) {
1087 map->mToPlace[index].cnt--;
1088 }
1089
1090 for (i = map->numPlaced; i--;) {
1091 const Tile* tile = map->mPlaced[i].tile;
1092 const int x = map->mPlaced[i].x;
1093 const int y = map->mPlaced[i].y;
1094 assert(i >= 0);
1095 assert(tile);
1096
1097 /* add the tile again*/
1098 for (ty = 0; ty < tile->h; ty++) {
1099 for (tx = 0; tx < tile->w; tx++) {
1100 assert(y + ty < MAX_RANDOM_MAP_HEIGHT);
1101 assert(x + tx < MAX_RANDOM_MAP_WIDTH);
1102
1103 SV_CombineAlternatives(&map->curMap[y + ty][x + tx], tile->spec[ty][tx]);
1104 }
1105 }
1106 }
1107
1108 if (idx)
1109 *idx = index;
1110
1111 if (pos)
1112 *pos = map->mPlaced[map->numPlaced].pos;
1113}
1114
1126static void SV_BuildMapStrings (const MapInfo* map, char* asmTiles, char* asmPos, bool print)
1127{
1128 const Assembly* mAsm = map->getCurrentAssembly();
1129
1130 for (int i = 0; i < map->numPlaced; i++) {
1131 const mPlaced_t* pl = &map->mPlaced[i];
1132
1133 if (sv_dumpmapassembly->integer)
1134 SV_DumpPlaced(map, i);
1135
1136 if (asmTiles[0])
1137 Q_strcat(asmTiles, MAX_TOKEN_CHARS * MAX_TILESTRINGS, " ");
1138 if (asmPos[0])
1140
1141 Q_strcat(asmTiles, MAX_TOKEN_CHARS * MAX_TILESTRINGS, "%s", pl->tile->id);
1142 Q_strcat(asmPos, MAX_TOKEN_CHARS * MAX_TILESTRINGS, "%i %i %i", (pl->x - mAsm->width / 2) * 8, (pl->y - mAsm->height / 2) * 8, 0);
1143 }
1144
1145 if (print) {
1146 Com_Printf("Map info - tiles used: %s\n", asmTiles);
1147 Com_Printf("Map info - tiles pos: %s\n", asmPos);
1148 Com_Printf("Map info - tiles count: %i\n", map->numPlaced);
1149 }
1150}
1151
1161static unsigned long SV_GapGetFlagsAtAbsPos (MapInfo* map, int tileCode, int mapW, int mapX, int mapY)
1162{
1163 const int pos = tileCode / TCM;
1164 const int ti = tileCode % TCM;
1165 const int posX = pos % mapW;
1166 const int posY = pos / mapW;
1167 const mToPlace_t* mToPlace = map->mToPlace;
1168 const Tile* tile = mToPlace[ti].tile;
1169
1170 return tile->spec[mapY - posY][mapX - posX];
1171}
1172
1184static int availableTiles[MAX_TILETYPES][2]; /* the 2nd dimension is index and count */
1185
1186static bool SV_AddMissingTiles_r (MapInfo* map, int rec, int posListCnt, short myPosList[], const Tile* prevTile, int prevX, int prevY)
1187{
1188 static int callCnt = 0;
1189 const Assembly* mAsm = map->getCurrentAssembly();
1190 const int mapW = mAsm->width;
1191 const mToPlace_t* mToPlace = map->mToPlace;
1192 int i, j = 0;
1193 int solids = 0; /* the # of places that the remaining tiles can theoretically cover */
1194 int availableTilesCnt = 0; /* the # of different tiles remaining in posTileList */
1195
1196 assert(rec < RMA2_MAX_REC);
1197 callCnt++;
1198
1200 int prevMaxX = 0, prevMaxY = 0;
1201 if (rec > 0) {
1202 prevMaxX = prevX + prevTile->w - 1;
1203 prevMaxY = prevY + prevTile->h - 1;
1204 }
1205
1207 for (i = 0; i < posListCnt; i++) {
1208 const int pos = myPosList[i] / TCM;
1209 const int ti = myPosList[i] % TCM;
1210 const int x = pos % mapW;
1211 const int y = pos / mapW;
1212
1213 if (mToPlace[ti].cnt >= mToPlace[ti].max)
1214 continue;
1215
1216 const Tile* cTile = mToPlace[ti].tile;
1217 bool ok = false;
1219 if (rec > 0) { /* the first recursion doesn't have a previous tile */
1220 if (x > prevMaxX || y > prevMaxY || prevX > x + cTile->w - 1 || prevY > y + cTile->h - 1)
1221 ok = true; /* tiles do not overlap, so tile will still fit */
1222 }
1223
1224 if (!ok) {
1225 ok = SV_FitTile(map, mToPlace[ti].tile, x, y);
1226 }
1227 if (ok) {
1228 /* store the posTile in our new list */
1229 assert(j < RMA2_MAX_TILEPOS);
1230 posTileList[rec][j] = myPosList[i];
1231 j++;
1232 /* store the tile index in the list of remaining tile types */
1233 int k;
1234 for (k = 0; k < availableTilesCnt; k++) {
1235 if (availableTiles[k][0] == ti) {
1236 availableTiles[k][1]++; /* inc count */
1237 break;
1238 }
1239 }
1240 if (k >= availableTilesCnt) { /* didn't find it in the list */
1241 availableTiles[availableTilesCnt][0] = ti; /* store the tile index */
1242 availableTiles[availableTilesCnt][1] = 1; /* init counter of places */
1243 availableTilesCnt++;
1244 }
1245 }
1246 }
1247
1249 int x, y;
1250 int gapCount = 0;
1251 for (y = 1; y < mAsm->height + 1; y++) {
1252 for (x = 1; x < mAsm->width + 1; x++)
1253 if (IS_SOLID(map->curMap[y][x]))
1254 gapList[x][y][0] = -1;
1255 else {
1256 gapCount++;
1257 gapList[x][y][0] = 0;
1258 }
1259 }
1260
1262 for (i = 0; i < availableTilesCnt; i++) {
1263 const int ti = availableTiles[i][0];
1264 const int allowed = mToPlace[ti].max - mToPlace[ti].cnt;
1265 const int possible = availableTiles[i][1];
1266 const int remaining = std::min(allowed, possible);
1267 solids += remaining * mToPlace[ti].tile->area;
1268 }
1269 if (solids < gapCount) {
1270 if (sv_rmadisplaythemap->integer) {
1271 const int missing = gapCount - solids;
1272 SV_RmaPrintMap(map);
1273 if (minMissingSolids > missing)
1274 minMissingSolids = missing;
1275 Com_Printf("out of solids (missing: %i min: %i)\n", missing, minMissingSolids);
1276 }
1277 return false;
1278 }
1279
1281 for (i = 0; i < j; i++) {
1282 const int pos = posTileList[rec][i] / TCM;
1283 const int ti = posTileList[rec][i] % TCM;
1284 const int x = pos % mapW;
1285 const int y = pos / mapW;
1286 const Tile* tile = mToPlace[ti].tile;
1287 int tx, ty;
1288 for (ty = 0; ty < tile->h; ty++) {
1289 for (tx = 0; tx < tile->w; tx++) {
1290 if (IS_SOLID(tile->spec[ty][tx])) {
1291 gapList[x + tx][y + ty][0] += 1;
1292 int cnt = gapList[x + tx][y + ty][0]; /* get the counter */
1293 if (cnt < GAPS + 1)
1294 gapList[x + tx][y + ty][cnt] = posTileList[rec][i]; /* remember the tilecode */
1295 }
1296 }
1297 }
1298 }
1299
1301 for (y = 1; y < mAsm->height + 1; y++) {
1302 for (x = 1; x < mAsm->width + 1; x++) {
1303 if (gapList[x][y][0] == 0) {
1304 if (sv_rmadisplaythemap->integer) {
1305 SV_RmaPrintMap(map);
1306 Com_Printf("uncovered gap: %i/%i\n", x, y);
1307 }
1308 return false;
1309 }
1310 }
1311 }
1312
1313#if 0
1315 for (y = 1; y < mAsm->height + 1; y++) {
1316 for (x = 1; x < mAsm->width + 1; x++) {
1317 Com_Printf("%2.i ", gapList[x][y][0]);
1318 }
1319 Com_Printf("\n");
1320 }
1321 Com_Printf("\n");
1322#endif
1323
1326 int g, h, line = 0;
1327 unsigned lineFlags = map->lineFlags; /* lineforming tiles, eg. water tiles in forest_large */
1328 unsigned nonLineFlags = (~lineFlags) ^ 1L;
1329 if (lineFlags)
1330 line = 1;
1331 for (; line >= 0; line--) { /* if there are lineforming tiles, process them first */
1332 for (g = 1; g <= GAPS; g++) { /* process the gaps with the least alternatives first */
1333 for (y = 1; y < mAsm->height + 1; y++) {
1334 for (x = 1; x < mAsm->width + 1; x++) {
1335 if (gapList[x][y][0] == g) { /* if this gap has the right amount of covering tiles */
1336 /* if we are looking for lines but the gap doesn't require a line-tile, skip */
1337 if (line && (map->curMap[y][x] & nonLineFlags))
1338 continue;
1339 for (h = 1; h <= g; h++) { /* circle through the alternatives stored for this gap */
1340 const int tc = gapList[x][y][h];
1341 const int pos = tc / TCM;
1342 const int ti = tc % TCM;
1343 const int px = pos % mapW;
1344 const int py = pos / mapW;
1345
1346 SV_AddTile(map, mToPlace[ti].tile, px, py, ti, pos);
1347#if PRINT_RMA_PROGRESS
1348 if (rec < 10)
1349 Com_Printf("GAPS: %i rec: %i chances: %i calls: %i\n", GAPS, rec, j, callCnt);
1350#endif
1351 if (SV_TestFilled(map))
1352 return true; /* this was the last tile we needed */
1353 if (SV_AddMissingTiles_r(map, rec + 1, j, posTileList[rec], mToPlace[ti].tile, px, py))
1354 return true; /* recursive placement succeeded */
1355
1356 /* tile was a dead end, remove it */
1357 SV_RemoveTile(map, nullptr, nullptr);
1358
1359 if (h >= g) {
1360#if 0
1361 if (sv_rmadisplaythemap->integer) {
1362 SV_RmaPrintMap(map);
1363 Com_Printf("no tile works for gap\n");
1364 }
1365#endif
1366 return false;
1367 }
1368 }
1369 }
1370 }
1371 }
1372 }
1373 }
1374
1376 for (i = 0; i < j; i++) {
1377 const int pos = posTileList[rec][i] / TCM;
1378 const int ti = posTileList[rec][i] % TCM;
1379 const int x = pos % mapW;
1380 const int y = pos / mapW;
1381
1382 SV_AddTile(map, mToPlace[ti].tile, x, y, ti, pos);
1383 if (SV_TestFilled(map))
1384 return true;
1385 if (SV_AddMissingTiles_r(map, rec + 1, j, posTileList[rec], mToPlace[ti].tile, x, y))
1386 return true;
1387 else
1388 SV_RemoveTile(map, nullptr, nullptr);
1389 }
1390 return false;
1391}
1392
1400static bool SV_GapListBuild (MapInfo* map, int tilePosListCnt)
1401{
1402 const Assembly* mAsm = map->getCurrentAssembly();
1403 const int mapW = mAsm->width;
1404 const mToPlace_t* mToPlace = map->mToPlace;
1405
1407 int x, y;
1408 for (y = 1; y < mAsm->height + 1; y++) {
1409 for (x = 1; x < mAsm->width + 1; x++)
1410 if (IS_SOLID(map->curMap[y][x]))
1411 gapList[x][y][0] = -1; /* the gap is solid already, so we don't need a counter. But we might need the info. */
1412 else
1413 gapList[x][y][0] = 0; /* the counter for this pos */
1414 }
1415
1416 /* check how well the tiles can cover the gaps */
1417 for (int i = 0; i < tilePosListCnt; i++) {
1418 const int pos = posTileList[0][i] / TCM;
1419 const int ti = posTileList[0][i] % TCM;
1420 x = pos % mapW;
1421 y = pos / mapW;
1422 Tile* tile = mToPlace[ti].tile;
1423 for (int ty = 0; ty < tile->h; ty++) {
1424 for (int tx = 0; tx < tile->w; tx++) {
1425 if (IS_SOLID(tile->spec[ty][tx])) {
1426 gapList[x + tx][y + ty][0] += 1;
1427 const int cnt = gapList[x + tx][y + ty][0]; /* get the counter */
1428 if (cnt < GAPS + 1)
1429 gapList[x + tx][y + ty][cnt] = posTileList[0][i]; /* remember the tilecode */
1430 }
1431 }
1432 }
1433 }
1434
1436 for (y = 1; y < mAsm->height + 1; y++) {
1437 for (x = 1; x < mAsm->width + 1; x++) {
1438 if (gapList[x][y][0] == 0)
1439 return false;
1440 }
1441 }
1442 return true;
1443}
1444
1455static bool SV_GapCheckNeighbour (MapInfo* map, int tc1, int mapW, int mapH, int nx, int ny)
1456{
1457 if (nx < 1)
1458 /* map border */
1459 return false;
1460 if (ny < 1)
1461 return false;
1462 if (nx > mapW)
1463 return false;
1464 if (ny > mapH)
1465 return false;
1466
1467 if (gapList[nx][ny][0] < 1)
1468 /* no tiles cover this gap, probably map border */
1469 return false;
1470 if (gapList[nx][ny][0] >= GAPS)
1471 /* if there are more tiles than we stored the tc's of,
1472 * we can not evaluate this neighbour. */
1473 return false;
1474
1475 bool flags1 = SV_GapGetFlagsAtAbsPos(map, tc1, mapW, nx, ny);
1476 if (IS_SOLID(flags1))
1477 /* nx/ny is part of tc1 itself */
1478 return false;
1479
1481 int h;
1482 for (h = 1; h <= gapList[nx][ny][0]; h++) {
1483 const int tc2 = gapList[nx][ny][h];
1484 const unsigned long flags2 = SV_GapGetFlagsAtAbsPos(map, tc2, mapW, nx, ny);
1485
1486 if (flags1 & flags2) {
1487 /* found at least one tile that would work */
1488 return false;
1489 }
1490 }
1491 return true;
1492}
1493
1500static int SV_GapListReduce (MapInfo* map)
1501{
1502 const Assembly* mAsm = map->getCurrentAssembly();
1503 const int mapW = mAsm->width;
1504 const int mapH = mAsm->height;
1505 int x, y;
1506 int n = 0;
1507
1509 for (y = 1; y < mapH + 1; y++) {
1510 for (x = 1; x < mapW + 1; x++) {
1511 if (gapList[x][y][0] < 1) /* solid ? */
1512 continue;
1513
1514 int g;
1515 for (g = 1; g <= gapList[x][y][0]; g++) {
1516 const int tc1 = gapList[x][y][g];
1517 if (g >= GAPS)
1518 break; /* there are more tiles than we stored the tc's of. */
1519
1520 /* check the neighbour to the right */
1521 if (SV_GapCheckNeighbour(map, tc1, mapW, mapH, x+1, y)) {
1522 posTileList[1][n] = tc1;
1523 n++;
1524 continue;
1525 }
1526 /* check the neighbour to the left */
1527 if (SV_GapCheckNeighbour(map, tc1, mapW, mapH, x-1, y)) {
1528 posTileList[1][n] = tc1;
1529 n++;
1530 continue;
1531 }
1532 /* check the upper neighbour */
1533 if (SV_GapCheckNeighbour(map, tc1, mapW, mapH, x, y+1)) {
1534 posTileList[1][n] = tc1;
1535 n++;
1536 continue;
1537 }
1538 /* check the neighbour below */
1539 if (SV_GapCheckNeighbour(map, tc1, mapW, mapH, x, y-1)) {
1540 posTileList[1][n] = tc1;
1541 n++;
1542 continue;
1543 }
1544 }
1545 }
1546 }
1547
1548 return n;
1549}
1550
1551#if PRINT_RMA_PROGRESS
1552 char mapStr[10000] = {0};
1553 char posStr[10000] = {0};
1554#endif
1572static bool SV_AddMissingTiles (MapInfo* map)
1573{
1574 static int attempts = 0; /* how often this function is called in the RMA process */
1575 const Assembly* mAsm = map->getCurrentAssembly();
1576 const int mapSize = mAsm->size; /* the # of grid squares in the assembly. A grid suare is usually 8x8 cells. */
1577 const int mapW = mAsm->width;
1578 const mToPlace_t* mToPlace = map->mToPlace;
1579 short posList[MAX_RANDOM_MAP_HEIGHT * MAX_RANDOM_MAP_WIDTH]; /* array of random positions */
1580 short tilenumList[MAX_TILETYPES]; /* array of tiles */
1581
1582 /* shuffle only once, the map will be build with that seed */
1583 RandomList(mapSize, posList);
1584 RandomList(map->numToPlace, tilenumList);
1585 attempts++;
1586
1587 /* check if the map is already filled */
1588 if (SV_TestFilled(map))
1589 return true;
1590
1592 int i, j, k, offs, num, n = 0;
1593 for (i = 0; i < mapSize; i++) {
1594 const int x = posList[i] % mapW;
1595 const int y = posList[i] / mapW;
1596
1597 /* only use positions that are on the grid */
1598 if (x % mAsm->dx != 0 || y % mAsm->dy != 0) {
1599 continue;
1600 }
1601 /* if we simply test the tiles in the same sequence for each pos, we get the 'boring maps' problem */
1602 /* So let's check them from a different starting point in the list each time */
1603 /* Example: if we have say 20 tiles, test eg. 13-20 first, then 1-12 */
1604 num = map->numToPlace;
1605 offs = rand() % num;
1606 for (k = offs; k < num + offs; k++) {
1607 const int ti = tilenumList[k % num];
1608
1609 if (mToPlace[ti].cnt >= mToPlace[ti].max)
1610 continue;
1611 if (SV_FitTile(map, mToPlace[ti].tile, x, y)) {
1612 posTileList[0][n] = posList[i] * TCM + ti;
1613 assert(n < RMA2_MAX_TILEPOS);
1614 n++;
1615 }
1616 }
1617 }
1618#if PRINT_RMA_PROGRESS
1619 Com_Printf("\nMapsize: %i tiles: %i chances: %i\n", mapSize, map->numToPlace, n);
1620 mapStr[0] = 0;
1621 posStr[0] = 0;
1622 if (map->numPlaced < 8)
1623 SV_BuildMapStrings(map, mapStr, posStr, true);
1624#endif
1626 bool eliminated = true;
1627 while (eliminated) { /* if we could eliminate one or more tiles, try again */
1628 eliminated = false;
1629#if 0
1630 /* print the posTileList */
1631 for (i = 0; i < n; i++) {
1632 Com_Printf("%2.i/%2.i ", posTileList[0][i] / TCM, posTileList[0][i] % TCM);
1633 if (!(i % 10))
1634 Com_Printf("\n");
1635 }
1636 Com_Printf("\n");
1637#endif
1638 bool covered = SV_GapListBuild(map, n);
1639
1640#if 0
1641 /* print the gapList */
1642 Com_Printf("\n");
1643 for (int x = 0; x < mAsm->width + 1; x++){
1644 for (int y = 0; y < mAsm->height + 1; y++){
1645 int cnt = gapList[x][y][0];
1646 Com_Printf("x:%i y:%i cnt:%i ", x, y, cnt);
1647 for (j = 0; j <= cnt + 3; j++) {
1648 Com_Printf("%i ", gapList[x][y][j]);
1649 }
1650 Com_Printf("\n");
1651 }
1652 }
1653#endif
1654
1655 if (!covered)
1656 return false; /* creating the gapList left one gap uncovered */
1657
1658 int m = SV_GapListReduce(map);
1659 if (m) { /* the number of tilepositions to discard */
1660 eliminated = true;
1661 for (j = 0; j < m; j++) {
1662 const int tc = posTileList[1][j]; /* SV_GapListReduce abuses the space for rec=1 to return it's result */
1663 int offset = 0;
1664 for (i = 0; i < n; i++) {
1665 if (posTileList[0][i] == tc) {
1666 offset = 1;
1667 continue;
1668 }
1669 posTileList[0][i - offset] = posTileList[0][i];
1670 }
1671 if (offset) /* only if we actually removed the tile */
1672 n--; /* reduce the counter of posTileList */
1673 }
1674 }
1675 }
1676
1677 minMissingSolids = 999;
1678 return SV_AddMissingTiles_r(map, 0, n, posTileList[0], nullptr, 0, 0);
1679}
1680
1698static bool SV_AddMapTiles (MapInfo* map)
1699{
1700 int idx; /* index in the array of available tiles */
1701 int pos; /* index in the array of random positions */
1702 const Assembly* mAsm = map->getCurrentAssembly();
1703 const int mapW = mAsm->width; /* width in x-direction */
1704 const int mapSize = mAsm->size; /* the # of grid squares in the assembly. A grid suare is usually 8x8 cells. */
1705 const int numToPlace = map->numToPlace;
1706 const mToPlace_t* mToPlace = map->mToPlace; /* pointer to a tile descriptor */
1707 short prList[MAX_RANDOM_MAP_HEIGHT * MAX_RANDOM_MAP_WIDTH]; /* array of random positions */
1708 const int start = map->numPlaced;
1709#ifdef DEBUG
1710 const mPlaced_t* mPlaced = map->mPlaced;
1711#endif
1712
1713#if PRINT_RMA_PROGRESS
1714 char mapStr[10000] = {0};
1715 char posStr[10000] = {0};
1716#endif
1717
1718 /* shuffle only once, the map will be build with that seed */
1719 RandomList(mapSize, prList);
1720
1721 pos = 0;
1722 idx = 0;
1723 while (idx < numToPlace) { /* for all tile-descriptors */
1724 while (mToPlace[idx].cnt < mToPlace[idx].min) {
1725 for (; pos < mapSize; pos++) {
1726 const int x = prList[pos] % mapW;
1727 const int y = prList[pos] / mapW;
1728 if (sv_threads->integer && sv_rma->integer == 1) {
1729 if (SDL_SemValue(mapSem) != 1) {
1730 /* someone else beat me to it */
1731 return true;
1732 }
1733 }
1734
1735 if ((x % mAsm->dx != 0) || (y % mAsm->dy != 0))
1736 continue;
1737
1738 if (SV_FitTile(map, mToPlace[idx].tile, x, y)) {
1739 /* add tile */
1740 SV_AddTile(map, mToPlace[idx].tile, x, y, idx, pos);
1741#if PRINT_RMA_PROGRESS
1742 mapStr[0] = 0;
1743 posStr[0] = 0;
1744 if (map->numPlaced < 6)
1745 SV_BuildMapStrings(map, mapStr, posStr, true);
1746#endif
1747 break;
1748 }
1749 }
1750 /* tile fits, try another tile of the same type */
1751 if (pos < mapSize)
1752 continue;
1753
1754 /* tile doesn't fit and no try left with this tile */
1755 if (!mToPlace[idx].cnt)
1756 break;
1757
1758 /* tile does not fit, restore last status - replace the last tile */
1759 assert(map->numPlaced > 0);
1760#ifdef DEBUG
1761 assert(idx == mPlaced[map->numPlaced - 1].idx);
1762#endif
1763 if (sv_rmadisplaythemap->integer) {
1764 SV_RmaPrintMap(map);
1765 Com_Printf("required tile doesn't fit\n");
1766 }
1767 SV_RemoveTile(map, &idx, &pos);
1768 pos++;
1769 }
1770
1771 /* tile fits, try next tile */
1772 if (pos < mapSize) {
1773 pos = 0; /* start at the beginning of the random positions array */
1774 idx++;
1775 } else {
1776 /* no more retries */
1777 if (start == map->numPlaced) {
1778 if (mAsm->numSeeds == 0 || map->retryCnt > 2) {
1779 Com_Error(ERR_DROP, "SV_AddMapTiles: Impossible to assemble map '%s' with assembly '%s'\n",
1780 map->getName(), mAsm->id ? mAsm->id : "");
1781 } else {
1782 Com_Printf("SV_AddMapTiles: Impossible to assemble map '%s' with assembly '%s' - retry with another seed\n",
1783 map->getName(), mAsm->id ? mAsm->id : "");
1784 return false;
1785 }
1786 }
1787 SV_RemoveTile(map, &idx, &pos);
1788 pos++;
1789 }
1790
1791 if (idx == numToPlace && !SV_AddMissingTiles(map)) {
1792 if (sv_rmadisplaythemap->integer) {
1793 SV_RmaPrintMap(map);
1794 Com_Printf("couldn't pad\n");
1795 }
1796 SV_RemoveTile(map, &idx, &pos);
1797 pos++;
1798 }
1799 }
1800
1801 return true;
1802}
1803
1809{
1810 const Assembly* mAsm = map->getCurrentAssembly();
1811
1812 map->numToPlace = 0;
1813 OBJZERO(map->mToPlace);
1814
1815 for (int i = 0; i < map->numTiles; i++) {
1816 if (mAsm->max[i]) {
1817 map->mToPlace[map->numToPlace].tile = &map->mTile[i];
1818 map->mToPlace[map->numToPlace].min = mAsm->min[i];
1819 map->mToPlace[map->numToPlace].max = mAsm->max[i];
1820 map->numToPlace++;
1821 }
1822 }
1823}
1824
1835static int SV_AssemblyThread (void* data)
1836{
1837 MapInfo* map = static_cast<MapInfo*>(data);
1838
1839 Com_SetRandomSeed(time(nullptr));
1840
1841 if (!SV_AddMapTiles(map)) {
1842 map->retryCnt++;
1843 }
1844
1845 /* the first thread to reach this point, gets the semaphore */
1846 if (SDL_SemTryWait(mapSem) != 0)
1847 return -1;
1848 SDL_LockMutex(mapLock);
1849
1850 assert(threadID == 0);
1851 threadID = SDL_ThreadID();
1852
1853 /* tell main we're done */
1854 SDL_CondSignal(mapCond);
1855 SDL_UnlockMutex(mapLock);
1856
1857 return 0;
1858}
1859
1885{
1886 SDL_Thread* threads[ASSEMBLE_THREADS];
1888 int i;
1889 static int timeout = 5000; /* wait for 5 sec initially, double it every time it times out */
1890 const int threadno = std::min(sv_threads->integer, ASSEMBLE_THREADS);
1891
1892 assert(mapLock == nullptr);
1893 mapLock = SDL_CreateMutex();
1894
1895 assert(mapCond == nullptr);
1896 mapCond = SDL_CreateCond();
1897
1898 threadID = 0;
1899 assert(mapSem == nullptr);
1900 mapSem = SDL_CreateSemaphore(1);
1901
1902 SDL_LockMutex(mapLock);
1903 for (i = 0; i < threadno; i++) {
1904 maps[i] = Mem_AllocType(MapInfo);
1905 memcpy(maps[i], map, sizeof(*map));
1906 threads[i] = Com_CreateThread(SV_AssemblyThread, "AssemblyThread", (void*) maps[i]);
1907 }
1908 while (threadID == 0) {
1909 /* if nobody is done after 5 sec, restart, double the timeout. */
1910 if (SDL_CondWaitTimeout(mapCond, mapLock, timeout) != 0) {
1911 Com_Printf("SV_ParallelSearch: timeout at %i ms, restarting\n", timeout);
1912 timeout += timeout;
1913 /* tell them all to die */
1914 if (SDL_SemTryWait(mapSem) != 0) {
1915 /* couldn't tell everyone to die, someone must have finished since the last line... */
1916 continue;
1917 }
1918 /* collect the dead */
1919 for (i = 0; i < threadno; i++) {
1920 SDL_WaitThread(threads[i], nullptr);
1921 }
1922 /* reset semaphore */
1923 SDL_SemPost(mapSem);
1924 /* start'em again */
1925 for (i = 0; i < threadno; i++) {
1926 memcpy(maps[i], map, sizeof(*map));
1927 threads[i] = Com_CreateThread(SV_AssemblyThread, "AssemblyThread", (void*) maps[i]);
1928 }
1929 } else {
1930 /* someone finished */
1931 assert(threadID != 0);
1932 }
1933 }
1934 SDL_UnlockMutex(mapLock);
1935 for (i = 0; i < threadno; i++) {
1936 if (SDL_GetThreadID(threads[i]) == threadID) {
1937 memcpy(map, maps[i], sizeof(*map));
1938 }
1939
1940 SDL_WaitThread(threads[i], nullptr);
1941 Mem_Free(maps[i]);
1942 }
1943
1944 /* cleanup, for possible next time */
1945 SDL_DestroySemaphore(mapSem);
1946 SDL_DestroyCond(mapCond);
1947 SDL_DestroyMutex(mapLock);
1948 mapLock = nullptr;
1949 mapSem = nullptr;
1950 mapCond = nullptr;
1951 threadID = 0;
1952 timeout = 5000;
1953
1954 return 0;
1955}
1956
1965static void SV_ParseUMP (const char* name, char* entityString, MapInfo* map, bool inherit)
1966{
1967 char filename[MAX_QPATH];
1968 byte* buf;
1969 const char* text, *token;
1970
1971 /* load the map info */
1972 Com_sprintf(filename, sizeof(filename), "maps/%s.ump", name);
1974 if (!buf)
1975 Com_Error(ERR_DROP, "SV_ParseUMP: Map assembly info '%s' not found", filename);
1976
1977 /* parse it */
1978 text = (const char*)buf;
1979 do {
1980 token = Com_Parse(&text);
1981 if (!text)
1982 break;
1983
1984 if (Q_streq(token, "extends")) {
1985 token = Com_Parse(&text);
1986 if (inherit)
1987 Com_Printf("SV_ParseUMP: Nested extends in %s 'extends %s' ignored\n", filename, token);
1988 else
1989 SV_ParseUMP(token, entityString, map, true);
1990 } else if (Q_streq(token, "base")) {
1991 token = Com_Parse(&text);
1992 if (inherit)
1993 Q_strncpyz(map->inheritBasePath, token, sizeof(map->inheritBasePath));
1994 else
1995 Q_strncpyz(map->basePath, token, sizeof(map->basePath));
1996 } else if (Q_streq(token, "line")) {
1997 token = Com_Parse(&text);
1998 const char* p = token;
1999 map->lineFlags = 0;
2000 while (*p) {
2001 map->lineFlags |= tileMask(*p);
2002 p++;
2003 }
2004 } else if (Q_streq(token, "tileset")) {
2005 if (map->numTileSets >= MAX_TILESETS)
2006 Com_Printf("SV_ParseUMP: Too many map tileset found in (%s)\n", filename);
2007 else if (SV_ParseMapTileSet(filename, &text, map, inherit))
2008 map->numTileSets++;
2009 } else if (Q_streq(token, "worldspawn")) {
2010 const char* start = nullptr;
2011 const int length = Com_GetBlock(&text, &start);
2012 if (length == -1) {
2013 Com_Printf("SV_ParseUMP: Not a valid worldspawn block in '%s'\n", filename);
2014 } else {
2015 const int worldSpawnLength = SV_GetConfigStringLength(CS_ENTITYSTRING);
2016 if (length >= worldSpawnLength)
2017 Com_Printf("SV_ParseUMP: worldspawn is too big - only %i characters are allowed", worldSpawnLength);
2018 else
2019 Q_strncpyz(entityString, start, length);
2020 }
2021 } else if (Q_streq(token, "tile")) {
2022 if (map->numTiles >= MAX_TILETYPES)
2023 Com_Printf("SV_ParseUMP: Too many map tile types (%s)\n", filename);
2024 else if (SV_ParseMapTile(filename, &text, map, inherit))
2025 map->numTiles++;
2026 } else if (Q_streq(token, "assembly")) {
2027 if (inherit) {
2028 Com_SkipBlock(&text);
2029 } else {
2030 if (map->numAssemblies >= MAX_MAPASSEMBLIES)
2031 Com_Printf("SV_ParseUMP: Too many map assemblies (%s)\n", filename);
2032 else if (SV_ParseAssembly(map, filename, &text, &map->assemblies[map->numAssemblies]))
2033 map->numAssemblies++;
2034 }
2035 } else if (token[0] == '{') {
2036 Com_Printf("SV_ParseUMP: Skipping unknown block\n");
2037 /* ignore unknown block */
2038 text = strchr(text, '}') + 1;
2039 if (!text)
2040 break;
2041 } else
2042 Com_Printf("SV_ParseUMP: Unknown token '%s' (%s)\n", token, filename);
2043 } while (text);
2044
2045 /* free the file */
2047}
2048
2049#if SORT_BY_SIZE
2050static int cmpTileAreaSize (const void* a, const void* b)
2051{
2052 if (((const mToPlace_t*) a)->tile->area > ((const mToPlace_t*) b)->tile->area)
2053 return -1;
2054 else if (((const mToPlace_t*) a)->tile->area == ((const mToPlace_t*) b)->tile->area)
2055 return 0;
2056 return 1;
2057}
2058#endif
2059
2060static MapInfo* SV_DoMapAssemble (MapInfo* map, const char* assembly, char* asmTiles, char* asmPos, const unsigned int seed, bool print)
2061{
2062 const Assembly* mAsm = map->getCurrentAssembly();
2063
2064 Com_DPrintf(DEBUG_SERVER, "Use assembly: '%s'\n", mAsm->id);
2065
2066 /* check size */
2067 assert(mAsm->width <= MAX_RANDOM_MAP_WIDTH);
2068 assert(mAsm->height <= MAX_RANDOM_MAP_HEIGHT);
2069
2071
2072#if SORT_BY_SIZE
2073 /* This is the perfect time to sort them by size, which helps RMA a lot.
2074 * This eliminates the need for a seedlist in oriental large completely.
2075 * Unfortunately, it doesn't do that for the others. Instead, it can slow down some maps quite a bit. */
2076 qsort(map->mToPlace, map->numToPlace, sizeof(mToPlace_t), cmpTileAreaSize);
2077#endif
2078
2079 /* assemble the map */
2080 map->numPlaced = 0;
2081 SV_ClearMap(map);
2082
2083 /* place fixed parts - defined in ump via fix parameter */
2084 for (int i = 0; i < mAsm->numFixed; i++)
2085 SV_AddTile(map, &map->mTile[mAsm->fT[i]], mAsm->fX[i], mAsm->fY[i], -1, -1);
2086
2087 if (sv_threads->integer && sv_rma->integer == 1) {
2088 const int oldCount = map->retryCnt;
2089 if (SV_ParallelSearch(map) < 0) {
2090 if (oldCount < map->retryCnt && mAsm->numSeeds > 0) {
2091 /* if we are allowed to restart the search with a fixed seed
2092 * from the assembly definition, do so */
2093 Com_SetRandomSeed(mAsm->seeds[rand() % mAsm->numSeeds]);
2094 return SV_DoMapAssemble(map, assembly, asmTiles, asmPos, seed, print);
2095 }
2096 Mem_Free(map);
2097 return nullptr;
2098 }
2099 } else {
2100 unsigned int seedUsed;
2101
2102 if (mAsm->numSeeds > 0) {
2103 /* if the map has a seedlist defined, use that */
2104 seedUsed = mAsm->seeds[rand() % mAsm->numSeeds];
2105 if (seed) {
2106 /* if a seed was passed, we are in cunit test mode */
2107 if (seed >= mAsm->numSeeds)
2108 /* if the given seed is outside the seedlist, assume that we already tested it and pretend that it's ok */
2109 return map;
2110 /* use the passed seed as an index into the seedlist */
2111 seedUsed = mAsm->seeds[seed % mAsm->numSeeds];
2112 }
2113 Com_Printf("Picked seed: %i for <%s>\n", seedUsed, assembly);
2114 } else {
2115 /* no seedlist */
2116 if (seed)
2117 seedUsed = seed;
2118 else
2119 seedUsed = rand() % 50; /* limit the possible seeds to (testable) values between 0 and 49 */
2120 }
2121 Com_SetRandomSeed(seedUsed);
2122
2123 if (!SV_AddMapTiles(map)) {
2124 map->retryCnt++;
2125 if (mAsm->numSeeds > 0) {
2126 /* if we are allowed to restart the search with a fixed seed
2127 * from the assembly definition, do so */
2128 Com_SetRandomSeed(mAsm->seeds[seed % mAsm->numSeeds]);
2129 return SV_DoMapAssemble(map, assembly, asmTiles, asmPos, seed, print);
2130 }
2131 return nullptr;
2132 }
2133 }
2134
2135 /* prepare map and pos strings */
2136 if (map->basePath[0])
2137 Com_sprintf(asmTiles, sizeof(map->basePath) + 1, "-%s", map->basePath);
2138
2139 asmPos[0] = 0;
2140
2141 /* generate the strings */
2142 SV_BuildMapStrings(map, asmTiles, asmPos, print);
2143
2144 return map;
2145}
2146
2167static MapInfo* SV_AssembleMap_ (const char* mapTheme, const char* assembly, char* asmTiles, char* asmPos, char* entityString, const unsigned int seed, bool print)
2168{
2169 MapInfo* map;
2170
2171 map = Mem_AllocType(MapInfo);
2172 map->setName(mapTheme);
2173
2174 SV_ParseUMP(mapTheme, entityString, map, false);
2175
2176 /* check for parsed tiles and assemblies */
2177 if (!map->numTiles)
2178 Com_Error(ERR_DROP, "No map tiles defined (%s)!", map->getName());
2179#ifdef DEBUG
2180 else
2181 Com_DPrintf(DEBUG_SERVER, "numTiles: %i\n", map->numTiles);
2182#endif
2183
2184 if (!map->numAssemblies)
2185 Com_Error(ERR_DROP, "No map assemblies defined (%s)!", map->getName());
2186#ifdef DEBUG
2187 else
2188 Com_DPrintf(DEBUG_SERVER, "numAssemblies: %i\n", map->numAssemblies);
2189#endif
2190
2191 /* use random assembly, if no valid one has been specified */
2192 map->asmIdx = rand() % map->numAssemblies;
2193
2194 /* overwrite with specified, if any */
2195 if (Q_strvalid(assembly)) {
2196 int i;
2197 for (i = 0; i < map->numAssemblies; i++)
2198 if (Q_streq(assembly, map->assemblies[i].id)) {
2199 map->asmIdx = i;
2200 break;
2201 }
2202 if (i == map->numAssemblies) {
2203 Com_Printf("SV_AssembleMap: Map assembly '%s' not found\n", assembly);
2204 }
2205 }
2206
2207 SV_DoMapAssemble(map, assembly, asmTiles, asmPos, seed, print);
2208
2209 return map;
2210}
2211
2212int SV_AssembleMap (const char* mapTheme, const char* assembly, char* asmTiles, char* asmPos, char* entityString, const unsigned int seed, bool print)
2213{
2214 MapInfo* map = SV_AssembleMap_ (mapTheme, assembly, asmTiles, asmPos, entityString, seed, print);
2215 int num = map->numPlaced;
2216 Mem_Free(map);
2217 return num;
2218}
2219
2220int SV_AssembleMapAndTitle (const char* mapTheme, const char* assembly, char* asmTiles, char* asmPos, char* entityString, const unsigned int seed, bool print, char* asmTitle)
2221{
2222 MapInfo* map = SV_AssembleMap_ (mapTheme, assembly, asmTiles, asmPos, entityString, seed, print);
2223 int num = map->numPlaced;
2224 strcpy(asmTitle, map->getCurrentAssemblyTitle());
2225 Mem_Free(map);
2226 return num;
2227}
2228
2229void SV_PrintAssemblyStats (const char* mapTheme, const char* asmName)
2230{
2231 MapInfo* theMap = Mem_AllocType(MapInfo);
2232 char mapAsmName[80];
2233 const char* p = mapTheme;
2234
2235 if (*p == '+')
2236 p++;
2237 else
2238 return;
2239
2240 SV_ParseUMP(p, nullptr, theMap, false);
2241 theMap->asmIdx = 0;
2242 /* overwrite with specified, if any */
2243 if (asmName && asmName[0]) {
2244 int j;
2245 for (j = 0; j < theMap->numAssemblies; j++)
2246 if (Q_streq(asmName, theMap->assemblies[j].id)) {
2247 theMap->asmIdx = j;
2248 break;
2249 }
2250 if (j == theMap->numAssemblies) {
2251 Com_Printf("testMapDefStatistic: Map assembly '%s' not found\n", asmName);
2252 }
2253 }
2254
2255 SV_PrepareTilesToPlace(theMap);
2256 const Assembly* assembly = theMap->getCurrentAssembly();
2257
2258 int required = 0;
2259 int solids = 0;
2260 for (int k = 0; k < theMap->numToPlace; k++) {
2261 required += theMap->mToPlace[k].min;
2262 solids += theMap->mToPlace[k].max * theMap->mToPlace[k].tile->area;
2263 }
2264
2265 Com_sprintf(mapAsmName, sizeof(mapAsmName), "%s %s", p, asmName);
2266 Com_Printf("%22.22s %2.i %2.i %2.i %2.i %3.i %3.i \n", mapAsmName, theMap->numTiles, theMap->numToPlace, required, assembly->numSeeds, assembly->size, solids);
2267}
Stores the parsed data of an assembly definition. See *.ump files.
Definition sv_rma.cpp:109
int width
Definition sv_rma.cpp:119
int size
Definition sv_rma.cpp:123
int numSeeds
Definition sv_rma.cpp:130
byte fT[MAX_FIXEDTILES]
Definition sv_rma.cpp:115
int seeds[MAX_ASSEMBLY_SEEDS]
Definition sv_rma.cpp:128
char id[MAX_VAR]
Definition sv_rma.cpp:111
byte fX[MAX_FIXEDTILES]
Definition sv_rma.cpp:116
byte max[MAX_TILETYPES]
Definition sv_rma.cpp:114
byte min[MAX_TILETYPES]
Definition sv_rma.cpp:113
int height
Definition sv_rma.cpp:119
byte fY[MAX_FIXEDTILES]
Definition sv_rma.cpp:117
int numFixed
Definition sv_rma.cpp:118
char title[MAX_VAR]
Definition sv_rma.cpp:112
int numAssemblies
Definition sv_rma.cpp:165
int numToPlace
Definition sv_rma.cpp:162
void setName(const char *mapTheme)
Definition sv_rma.cpp:187
int numTiles
Definition sv_rma.cpp:174
unsigned long curMap[MAX_RANDOM_MAP_HEIGHT][MAX_RANDOM_MAP_WIDTH]
Stores the alternatives information for the assembled map.
Definition sv_rma.cpp:158
mToPlace_t mToPlace[MAX_TILETYPES]
Stores the Tiles to Place in the map.
Definition sv_rma.cpp:161
const char * getCurrentAssemblyTitle() const
Definition sv_rma.cpp:193
const Assembly * getCurrentAssembly() const
Definition sv_rma.cpp:184
TileSet tileSets[MAX_TILESETS]
Definition sv_rma.cpp:170
int retryCnt
Definition sv_rma.cpp:182
mPlaced_t mPlaced[MAX_MAPTILES]
Definition sv_rma.cpp:167
int numTileSets
Definition sv_rma.cpp:171
Assembly assemblies[MAX_MAPASSEMBLIES]
Definition sv_rma.cpp:164
int asmIdx
Definition sv_rma.cpp:180
char inheritBasePath[MAX_QPATH]
Definition sv_rma.cpp:178
Tile mTile[MAX_TILETYPES]
Definition sv_rma.cpp:173
char basePath[MAX_QPATH]
Definition sv_rma.cpp:177
unsigned long lineFlags
Definition sv_rma.cpp:175
int numPlaced
Definition sv_rma.cpp:168
const char * getName() const
Definition sv_rma.cpp:190
char name[MAX_VAR]
Definition sv_rma.cpp:155
Stores the parsed data for a map tile. (See *.ump files).
Definition sv_rma.cpp:86
int area
Definition sv_rma.cpp:91
int h
Definition sv_rma.cpp:90
unsigned long spec[MAX_TILESIZE][MAX_TILESIZE]
Definition sv_rma.cpp:89
int w
Definition sv_rma.cpp:90
char id[MAX_VAR]
Definition sv_rma.cpp:88
A list of tiles with the same size and neighbouring requirements to randomly pick from.
Definition sv_rma.cpp:95
char id[MAX_VAR]
Definition sv_rma.cpp:97
char tiles[MAX_TILESETTILES][MAX_VAR]
Definition sv_rma.cpp:98
int numTiles
Definition sv_rma.cpp:99
void Com_DPrintf(int level, const char *fmt,...)
A Com_Printf that only shows up if the "developer" cvar is set.
Definition common.cpp:440
void Com_Error(int code, const char *fmt,...)
Definition common.cpp:459
void Com_SetRandomSeed(unsigned int seed)
Definition common.cpp:1016
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
cvar_t * sv_maxclients
Definition g_main.cpp:43
#define ERR_DROP
Definition common.h:211
cvar_t * Cvar_Set(const char *varName, const char *value,...)
Sets a cvar value.
Definition cvar.cpp:615
cvar_t * Cvar_FindVar(const char *varName)
Searches for a cvar given by parameter.
Definition cvar.cpp:106
#define MAX_TOKEN_CHARS
Definition defines.h:372
#define DEBUG_SERVER
Definition defines.h:60
int FS_LoadFile(const char *path, byte **buffer)
Filenames are relative to the quake search path.
Definition files.cpp:384
void FS_FreeFile(void *buffer)
Definition files.cpp:411
#define MAX_QPATH
Definition filesys.h:40
voidpf void uLong size
Definition ioapi.h:42
const char * filename
Definition ioapi.h:41
voidpf void * buf
Definition ioapi.h:42
voidpf uLong offset
Definition ioapi.h:45
static struct mdfour * m
Definition md4.cpp:35
#define Mem_Free(ptr)
Definition mem.h:35
#define Mem_AllocType(type)
Definition mem.h:39
const char * Com_Parse(const char *data_p[], char *target, size_t size, bool replaceWhitespaces)
Parse a token out of a string.
Definition parse.cpp:107
void Com_SkipBlock(const char **text)
Skips a block of {} in our script files.
Definition parse.cpp:253
int Com_GetBlock(const char **text, const char **start)
Get the start and end point of a block in the given text.
Definition parse.cpp:272
Shared parsing functions.
#define CS_ENTITYSTRING
Definition q_shared.h:324
#define MAX_TILESTRINGS
Definition q_shared.h:298
QGL_EXTERN GLsizei const GLvoid * data
Definition r_gl.h:89
QGL_EXTERN GLuint GLsizei GLsizei * length
Definition r_gl.h:110
QGL_EXTERN GLuint index
Definition r_gl.h:110
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition r_gl.h:110
const char * Com_EParse(const char **text, const char *errhead, const char *errinfo, char *target, size_t size)
Parsing function that prints an error message when there is no text in the buffer.
Definition scripts.cpp:277
Main server include file.
cvar_t * sv_rmadisplaythemap
display a character graphic of the tiles placed when RMA2 reaches a dead end.
Definition sv_main.cpp:50
int SV_GetConfigStringLength(int index)
Definition sv_main.cpp:85
cvar_t * sv_threads
Definition sv_main.cpp:48
cvar_t * sv_rma
Definition sv_main.cpp:49
cvar_t * sv_dumpmapassembly
Definition sv_main.cpp:47
#define Q_strvalid(string)
Definition shared.h:141
#define Q_streq(a, b)
Definition shared.h:136
#define Q_strncasecmp(s1, s2, n)
Definition shared.h:132
#define OBJZERO(obj)
Definition shared.h:178
#define OBJSET(obj, val)
Definition shared.h:177
#define MAX_VAR
Definition shared.h:36
#define lengthof(x)
Definition shared.h:105
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
void Q_strcat(char *dest, size_t destsize, const char *format,...)
Safely (without overflowing the destination buffer) concatenates two strings.
Definition shared.cpp:475
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
This is a cvar definition. Cvars can be user modified and used in our menus e.g.
Definition cvar.h:71
char * string
Definition cvar.h:73
char * name
Definition cvar.h:72
Defines a placed tile.
Definition sv_rma.cpp:147
const Tile * tile
Definition sv_rma.cpp:148
Defines a tile to place.
Definition sv_rma.cpp:137
Tile * tile
Definition sv_rma.cpp:138
#define RMA2_MAX_REC
max # of recursions
Definition sv_rma.cpp:44
#define MMH
Definition sv_rma.cpp:293
static const TileSet * SV_GetMapTileSet(const MapInfo *map, const char *tileSetName)
Definition sv_rma.cpp:403
int SV_AssembleMapAndTitle(const char *mapTheme, const char *assembly, char *asmTiles, char *asmPos, char *entityString, const unsigned int seed, bool print, char *asmTitle)
Definition sv_rma.cpp:2220
static short posTileList[RMA2_MAX_REC][RMA2_MAX_TILEPOS]
array of working random tile positions, 50 recursions
Definition sv_rma.cpp:61
static MapInfo * SV_AssembleMap_(const char *mapTheme, const char *assembly, char *asmTiles, char *asmPos, char *entityString, const unsigned int seed, bool print)
Assembles a "random" map and parses the *.ump files for assembling the "random" maps and places the '...
Definition sv_rma.cpp:2167
static void SV_RemoveTile(MapInfo *map, int *idx, int *pos)
Rebuilds a assembled map up to the previous tile.
Definition sv_rma.cpp:1073
static int cmpTileAreaSize(const void *a, const void *b)
Definition sv_rma.cpp:2050
#define ALL_TILES
Definition sv_rma.cpp:218
static const Tile * SV_GetMapTile(const MapInfo *map, const char *tileName)
Definition sv_rma.cpp:414
#define MAX_RANDOM_MAP_HEIGHT
Definition sv_rma.cpp:66
#define TCM
tile code multiplier. For the various debug printfs we want a number that we can easily divide throug...
Definition sv_rma.cpp:59
static void SV_GetTilesFromTileSet(const MapInfo *map, const char *filename, const char **text, Assembly *a)
Definition sv_rma.cpp:672
static bool SV_AddMissingTiles(MapInfo *map)
Tries to fill the missing tiles of the current map. While the 2010 algo used a 'by chance'-algo,...
Definition sv_rma.cpp:1572
#define MMW
Definition sv_rma.cpp:292
static const char * SV_GetCvarToken(const MapInfo *map, const Assembly *a, const char *token, const char *filename, const char **text, const char *errhead)
Tries to extract a tile name from a cvar - the cvar value must start with a '+'.
Definition sv_rma.cpp:572
#define MAX_MAPASSEMBLIES
Definition sv_rma.cpp:76
static bool SV_TestFilled(const MapInfo *map)
Checks if the map is completely filled.
Definition sv_rma.cpp:976
static void SV_DumpPlaced(const MapInfo *map, int pl)
Debug function to dump the map location of a placed tile.
Definition sv_rma.cpp:992
static void SV_RmaPrintMap(const MapInfo *map)
Definition sv_rma.cpp:294
static int SV_AssemblyThread(void *data)
The main function for the threads that try to create random map assemblies in parallel.
Definition sv_rma.cpp:1835
static void SV_ClearMap(MapInfo *map)
Reset the map to empty state.
Definition sv_rma.cpp:910
static const char * SV_GetTileFromTileSet(const MapInfo *map, const char *filename, const char **text, const Assembly *a)
Definition sv_rma.cpp:616
#define MAX_TILETYPES
Definition sv_rma.cpp:79
static bool SV_GapCheckNeighbour(MapInfo *map, int tc1, int mapW, int mapH, int nx, int ny)
Find a tile that meets the requirements of tc1 at a given pos.
Definition sv_rma.cpp:1455
static void SV_TileMaskToString(unsigned long m, char *str)
Definition sv_rma.cpp:261
static int minMissingSolids
Definition sv_rma.cpp:70
static unsigned long SV_GapGetFlagsAtAbsPos(MapInfo *map, int tileCode, int mapW, int mapX, int mapY)
get the specs of a tile at map-x/y if it was placed where tileCode indicates
Definition sv_rma.cpp:1161
static SDL_sem * mapSem
Definition sv_rma.cpp:71
#define MAX_FIXEDTILES
Definition sv_rma.cpp:83
static Uint32 threadID
Definition sv_rma.cpp:74
static void SV_PrepareTilesToPlace(MapInfo *map)
Prepare the list of tiles to place.
Definition sv_rma.cpp:1808
void SV_PrintAssemblyStats(const char *mapTheme, const char *asmName)
Definition sv_rma.cpp:2229
static int SV_ParallelSearch(MapInfo *map)
Spawn ASSEMBLE_THREADS threads to try and assemble a map. The first map complete gets returned....
Definition sv_rma.cpp:1884
#define MAX_ASSEMBLY_SEEDS
Definition sv_rma.cpp:102
static void SV_CombineAlternatives(unsigned long *mapAlts, const unsigned long tileAlts)
Combines the alternatives/connection info of a map with a tile and sets the rating.
Definition sv_rma.cpp:892
static MapInfo * SV_DoMapAssemble(MapInfo *map, const char *assembly, char *asmTiles, char *asmPos, const unsigned int seed, bool print)
Definition sv_rma.cpp:2060
static unsigned long tileMask(const char chr)
Convert to tile spec - normalize the characters.
Definition sv_rma.cpp:245
static SDL_cond * mapCond
Definition sv_rma.cpp:72
static int SV_GapListReduce(MapInfo *map)
Tries to find tiles that exclude all of their neighbours This is called only once,...
Definition sv_rma.cpp:1500
static void RandomList(const int n, short *list)
Fills a list with random values between 0 and n.
Definition sv_rma.cpp:203
#define GAPS
the # of different tiles we can store for a gap
Definition sv_rma.cpp:64
static short gapList[MAX_RANDOM_MAP_HEIGHT][MAX_RANDOM_MAP_HEIGHT][GAPS+1]
for every x/y we can store the tiles that can cover that place here
Definition sv_rma.cpp:68
static bool SV_ParseAssemblySeeds(MapInfo *map, const char *filename, const char **text, Assembly *a)
Parses a list of working seeds to assemble this rma assembly.
Definition sv_rma.cpp:644
static bool SV_ParseMapTileSet(const char *filename, const char **text, MapInfo *map, bool inherit)
Parsed a tileset definition out of the ump-files.
Definition sv_rma.cpp:429
static SDL_mutex * mapLock
Definition sv_rma.cpp:73
#define RMA2_MAX_TILEPOS
max # of valid tile/position combinations
Definition sv_rma.cpp:57
static bool SV_GapListBuild(MapInfo *map, int tilePosListCnt)
Builds a list of map positions (gaps) and the tiles that can cover them.
Definition sv_rma.cpp:1400
static bool SV_AddMapTiles(MapInfo *map)
Tries to build the map There are 3 categories of tiles:
Definition sv_rma.cpp:1698
static bool SV_AddMissingTiles_r(MapInfo *map, int rec, int posListCnt, short myPosList[], const Tile *prevTile, int prevX, int prevY)
Definition sv_rma.cpp:1186
#define MAX_TILESETTILES
Definition sv_rma.cpp:81
int SV_AssembleMap(const char *mapTheme, const char *assembly, char *asmTiles, char *asmPos, char *entityString, const unsigned int seed, bool print)
Definition sv_rma.cpp:2212
#define MAX_RANDOM_MAP_WIDTH
Definition sv_rma.cpp:65
#define ASSEMBLE_THREADS
Definition sv_rma.cpp:37
#define ACW
Definition sv_rma.cpp:290
static int availableTiles[MAX_TILETYPES][2]
Select the next tile to place and place it (recursively).
Definition sv_rma.cpp:1184
#define ACH
Definition sv_rma.cpp:291
static bool SV_FitTile(const MapInfo *map, const Tile *tile, const int x, const int y)
Checks if a given map-tile fits into the empty space (in a given location) of a map.
Definition sv_rma.cpp:926
static void SV_BuildMapStrings(const MapInfo *map, char *asmTiles, char *asmPos, bool print)
Creates the mapstrings as known from the ufoconsole.log and optionally prints them....
Definition sv_rma.cpp:1126
static bool SV_ParseMapTile(const char *filename, const char **text, MapInfo *map, bool inherit)
Parsed a tile definition out of the ump-files.
Definition sv_rma.cpp:488
static void SV_ParseUMP(const char *name, char *entityString, MapInfo *map, bool inherit)
Parses an ump file that contains the random map definition.
Definition sv_rma.cpp:1965
#define MAX_TILESIZE
Definition sv_rma.cpp:82
#define IS_SOLID(x)
Definition sv_rma.cpp:219
static bool SV_ParseAssembly(MapInfo *map, const char *filename, const char **text, Assembly *a)
Parses an assembly block.
Definition sv_rma.cpp:727
#define MAX_TILESETS
Definition sv_rma.cpp:80
static void SV_AddTile(MapInfo *map, const Tile *tile, int x, int y, int idx, int pos)
Adds a new map-tile to an assembled map. Also adds the tile to the placed-tiles list.
Definition sv_rma.cpp:1028
char posStr[MAX_TOKEN_CHARS *MAX_TILESTRINGS]
char mapStr[MAX_TOKEN_CHARS *MAX_TILESTRINGS]
SDL_Thread * Com_CreateThread(int(*fn)(void *), const char *name, void *data=nullptr)
Definition thread.h:5