1 /*
2 * XPilot NG, a multiplayer space war game.
3 *
4 * Copyright (C) 2003-2004 Kristian S�derblom <kps@users.sourceforge.net>
5 *
6 * Copyright (C) 1991-2001 by
7 *
8 * Bj�rn Stabell <bjoern@xpilot.org>
9 * Ken Ronny Schouten <ken@xpilot.org>
10 * Bert Gijsbers <bert@xpilot.org>
11 * Dick Balaska <dick@xpilot.org>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 */
27
28 #include "xpserver.h"
29
30 static int Compress_map(unsigned char *map, size_t size);
31
32 static void Xpmap_treasure_to_polygon(int treasure_ind);
33 static void Xpmap_target_to_polygon(int target_ind);
34 static void Xpmap_cannon_to_polygon(int cannon_ind);
35 static void Xpmap_wormhole_to_polygon(int wormhole_ind);
36 static void Xpmap_friction_area_to_polygon(int fa_ind);
37
38 static bool compress_maps = true;
39
Xpmap_extra_error(int line_num)40 static void Xpmap_extra_error(int line_num)
41 {
42 static int prev_line_num, error_count;
43 const int max_error = 5;
44
45 if (line_num > prev_line_num) {
46 prev_line_num = line_num;
47 if (++error_count <= max_error)
48 warn("Map file contains extraneous characters on line %d",
49 line_num);
50 else if (error_count - max_error == 1)
51 warn("And so on...");
52 }
53 }
54
55
Xpmap_missing_error(int line_num)56 static void Xpmap_missing_error(int line_num)
57 {
58 static int prev_line_num, error_count;
59 const int max_error = 5;
60
61 if (line_num > prev_line_num) {
62 prev_line_num = line_num;
63 if (++error_count <= max_error)
64 warn("Not enough map data on map data line %d", line_num);
65 else if (error_count - max_error == 1)
66 warn("And so on...");
67 }
68 }
69
70
71
72 /*
73 * Compress the map data using a simple Run Length Encoding algorithm.
74 * If there is more than one consecutive byte with the same type
75 * then we set the high bit of the byte and then the next byte
76 * gives the number of repetitions.
77 * This works well for most maps which have lots of series of the
78 * same map object and is simple enough to got implemented quickly.
79 */
Compress_map(unsigned char * map,size_t size)80 static int Compress_map(unsigned char *map, size_t size)
81 {
82 int i, j, k;
83
84 for (i = j = 0; i < (int)size; i++, j++) {
85 if (i + 1 < (int)size
86 && map[i] == map[i + 1]) {
87 for (k = 2; i + k < (int)size; k++) {
88 if (map[i] != map[i + k])
89 break;
90 if (k == 255)
91 break;
92 }
93 map[j] = (map[i] | SETUP_COMPRESSED);
94 map[++j] = k;
95 i += k - 1;
96 } else
97 map[j] = map[i];
98 }
99 return j;
100 }
101
102
Create_blockmap_from_polygons(void)103 void Create_blockmap_from_polygons(void)
104 {
105 int i, h, type;
106 blkpos_t blk;
107 clpos_t pos;
108 shape_t r_wire, u_wire, l_wire, d_wire;
109 clpos_t r_coords[3], u_coords[3], l_coords[3], d_coords[3];
110
111 r_wire.num_points = 3;
112 u_wire.num_points = 3;
113 l_wire.num_points = 3;
114 d_wire.num_points = 3;
115
116 for (i = 0; i < 3; i++)
117 r_wire.pts[i] = &r_coords[i];
118 for (i = 0; i < 3; i++)
119 u_wire.pts[i] = &u_coords[i];
120 for (i = 0; i < 3; i++)
121 l_wire.pts[i] = &l_coords[i];
122 for (i = 0; i < 3; i++)
123 d_wire.pts[i] = &d_coords[i];
124
125 /*
126 * Block is divided to 4 parts, r, u, l and d, the middle of the block
127 * looking like this. Each char represents a square click. The middle
128 * of the block is at 'x', square clicks with 'x' or ' ' are considered
129 * not part of any of the block parts r, u, l or d.
130 *
131 * uuu
132 * l U r
133 * lLxRr
134 * l D r
135 * ddd
136 *
137 */
138
139 h = BLOCK_CLICKS / 2 - 2;
140
141 /* right part of block */
142 r_coords[0].cx = 0;
143 r_coords[0].cy = 0; /* this is the R position in the block */
144 r_coords[1].cx = h;
145 r_coords[1].cy = -h;
146 r_coords[2].cx = h;
147 r_coords[2].cy = h;
148
149 /* up part of block */
150 u_coords[0].cx = 0;
151 u_coords[0].cy = 0;
152 u_coords[1].cx = h;
153 u_coords[1].cy = h;
154 u_coords[2].cx = -h;
155 u_coords[2].cy = h;
156
157 /* left part of block */
158 l_coords[0].cx = 0;
159 l_coords[0].cy = 0;
160 l_coords[1].cx = -h;
161 l_coords[1].cy = h;
162 l_coords[2].cx = -h;
163 l_coords[2].cy = -h;
164
165 /* down part of block */
166 d_coords[0].cx = 0;
167 d_coords[0].cy = 0;
168 d_coords[1].cx = -h;
169 d_coords[1].cy = -h;
170 d_coords[2].cx = h;
171 d_coords[2].cy = -h;
172
173 /*
174 * Create blocks out of polygons.
175 */
176 for (blk.by = 0; blk.by < world->y; blk.by++)
177 for (blk.bx = 0; blk.bx < world->x; blk.bx++)
178 World_set_block(blk, SPACE);
179
180
181 for (blk.by = 0; blk.by < world->bheight_floor; blk.by++) {
182 for (blk.bx = 0; blk.bx < world->bwidth_floor; blk.bx++) {
183 int num_inside = 0;
184 bool r_inside = false, u_inside = false;
185 bool l_inside = false, d_inside = false;
186
187 pos = Block_get_center_clpos(blk);
188
189 if (shape_is_inside(pos.cx+1, pos.cy, 0, NULL, &r_wire, 0) == 0) {
190 r_inside = true;
191 num_inside++;
192 }
193 if (shape_is_inside(pos.cx, pos.cy+1, 0, NULL, &u_wire, 0) == 0) {
194 u_inside = true;
195 num_inside++;
196 }
197 if (shape_is_inside(pos.cx-1, pos.cy, 0, NULL, &l_wire, 0) == 0) {
198 l_inside = true;
199 num_inside++;
200 }
201 if (shape_is_inside(pos.cx, pos.cy-1, 0, NULL, &d_wire, 0) == 0) {
202 d_inside = true;
203 num_inside++;
204 }
205
206 if (num_inside > 2)
207 World_set_block(blk, FILLED);
208
209 if (num_inside == 2) {
210 if (r_inside && u_inside)
211 World_set_block(blk, REC_RU);
212 if (u_inside && l_inside)
213 World_set_block(blk, REC_LU);
214 if (l_inside && d_inside)
215 World_set_block(blk, REC_LD);
216 if (d_inside && r_inside)
217 World_set_block(blk, REC_RD);
218 if (u_inside && d_inside)
219 World_set_block(blk, FILLED);
220 if (r_inside && l_inside)
221 World_set_block(blk, FILLED);
222 }
223
224 if (num_inside == 1) {
225 if (r_inside)
226 World_set_block(blk, REC_RU);
227 if (u_inside)
228 World_set_block(blk, REC_LU);
229 if (l_inside)
230 World_set_block(blk, REC_LD);
231 if (d_inside)
232 World_set_block(blk, REC_RD);
233 }
234 }
235 }
236
237 /*
238 * Create blocks out of map objects. Note that some of these
239 * may be in the same block, which might cause a client error.
240 */
241 for (i = 0; i < Num_fuels(); i++) {
242 fuel_t *fs = Fuel_by_index(i);
243
244 blk = Clpos_to_blkpos(fs->pos);
245 World_set_block(blk, FUEL);
246 }
247
248 for (i = 0; i < Num_asteroidConcs(); i++) {
249 asteroid_concentrator_t *aconc = AsteroidConc_by_index(i);
250
251 blk = Clpos_to_blkpos(aconc->pos);
252 World_set_block(blk, ASTEROID_CONCENTRATOR);
253 }
254
255 for (i = 0; i < Num_itemConcs(); i++) {
256 item_concentrator_t *iconc = ItemConc_by_index(i);
257
258 blk = Clpos_to_blkpos(iconc->pos);
259 World_set_block(blk, ITEM_CONCENTRATOR);
260 }
261
262 for (i = 0; i < Num_wormholes(); i++) {
263 wormhole_t *wh = Wormhole_by_index(i);
264
265 blk = Clpos_to_blkpos(wh->pos);
266 World_set_block(blk, WORMHOLE);
267 }
268
269 /* find balltargets */
270 for (blk.by = 0; blk.by < world->bheight_floor; blk.by++) {
271 for (blk.bx = 0; blk.bx < world->bwidth_floor; blk.bx++) {
272 int group;
273 group_t *gp;
274
275 pos = Block_get_center_clpos(blk);
276 group = shape_is_inside(pos.cx, pos.cy,
277 BALL_BIT, NULL, &filled_wire, 0);
278 if (group == NO_GROUP || group == 0)
279 continue;
280 gp = groupptr_by_id(group);
281 if (gp == NULL)
282 continue;
283 if (gp->type == TREASURE && gp->hitmask == NONBALL_BIT)
284 World_set_block(blk, TREASURE);
285 }
286 }
287
288 /*
289 * Handle bases. Note that on polygon maps there can be several
290 * bases on the area of a block. To handle this so that old clients
291 * won't get "Bad homebase index" error, we put excess bases somewhere
292 * else than on the same block.
293 */
294
295 /*
296 * First mark all blocks having a base.
297 * We use a base attractor for this.
298 */
299 for (i = 0; i < Num_bases(); i++) {
300 base_t *base = Base_by_index(i);
301
302 blk = Clpos_to_blkpos(base->pos);
303 type = World_get_block(blk);
304
305 /* don't put the base on top of a fuel or treasure */
306 if (type == FUEL || type == TREASURE)
307 continue;
308 World_set_block(blk, BASE_ATTRACTOR);
309 }
310
311 /*
312 * Put bases where there are base attractors or somewhere else
313 * if the block already has some other important type.
314 */
315 for (i = 0; i < Num_bases(); i++) {
316 base_t *base = Base_by_index(i);
317 bool done;
318
319 blk = Clpos_to_blkpos(base->pos);
320 type = World_get_block(blk);
321 done = false;
322
323 if (type == FUEL || type == TREASURE || type == BASE) {
324 /*
325 * The block where the base should be put already has some
326 * important type. We need to put this base somewhere else.
327 * Let's just line up excess bases close to the origin of the map.
328 */
329 for (blk.by = 0; blk.by < world->y; blk.by++) {
330 for (blk.bx = 0; blk.bx < world->x; blk.bx++) {
331 type = World_get_block(blk);
332 /*
333 * Check for base attractor here too because we might
334 * have marked this block in the earlier loop over all
335 * bases.
336 */
337 if (type == FUEL || type == TREASURE
338 || type == BASE || type == BASE_ATTRACTOR)
339 continue;
340 /* put base attractor here so that assert is happy */
341 type = BASE_ATTRACTOR;
342 World_set_block(blk, type);
343 done = true;
344 break;
345 }
346 if (done)
347 break;
348 }
349
350 /* this probably doesn't happen very often */
351 if (!done)
352 fatal("Create_blockmap_from_polygons:\n"
353 "Couldn't find any place on map to put base on "
354 "(%d, %d).", base->pos.cx, base->pos.cy);
355 }
356
357 assert(type == BASE_ATTRACTOR);
358 World_set_block(blk, BASE);
359 }
360 }
361
Xpmap_init_setup(void)362 setup_t *Xpmap_init_setup(void)
363 {
364 int i, x, y, team, type = -1, dir, wtype;
365 int wormhole_i = 0, treasure_i = 0, target_i = 0, base_i = 0, cannon_i = 0;
366 unsigned char *mapdata, *mapptr;
367 size_t size, numblocks;
368 setup_t *setup;
369
370 numblocks = world->x * world->y;
371 if ((mapdata = XMALLOC(unsigned char, numblocks)) == NULL) {
372 error("No memory for mapdata");
373 return NULL;
374 }
375 memset(mapdata, SETUP_SPACE, numblocks);
376 mapptr = mapdata;
377 errno = 0;
378 for (x = 0; x < world->x; x++) {
379 for (y = 0; y < world->y; y++, mapptr++) {
380 type = world->block[x][y];
381 switch (type) {
382 case ACWISE_GRAV:
383 case CWISE_GRAV:
384 case POS_GRAV:
385 case NEG_GRAV:
386 case UP_GRAV:
387 case DOWN_GRAV:
388 case RIGHT_GRAV:
389 case LEFT_GRAV:
390 if (!options.gravityVisible)
391 type = SPACE;
392 break;
393 case WORMHOLE:
394 if (!options.wormholeVisible)
395 type = SPACE;
396 break;
397 case ITEM_CONCENTRATOR:
398 if (!options.itemConcentratorVisible)
399 type = SPACE;
400 break;
401 case ASTEROID_CONCENTRATOR:
402 if (!options.asteroidConcentratorVisible)
403 type = SPACE;
404 break;
405 case FRICTION:
406 if (!options.blockFrictionVisible)
407 type = SPACE;
408 else
409 type = DECOR_FILLED;
410 break;
411 default:
412 break;
413 }
414 switch (type) {
415 case SPACE: *mapptr = SETUP_SPACE; break;
416 case FILLED: *mapptr = SETUP_FILLED; break;
417 case REC_RU: *mapptr = SETUP_REC_RU; break;
418 case REC_RD: *mapptr = SETUP_REC_RD; break;
419 case REC_LU: *mapptr = SETUP_REC_LU; break;
420 case REC_LD: *mapptr = SETUP_REC_LD; break;
421 case FUEL: *mapptr = SETUP_FUEL; break;
422 case ACWISE_GRAV: *mapptr = SETUP_ACWISE_GRAV; break;
423 case CWISE_GRAV: *mapptr = SETUP_CWISE_GRAV; break;
424 case POS_GRAV: *mapptr = SETUP_POS_GRAV; break;
425 case NEG_GRAV: *mapptr = SETUP_NEG_GRAV; break;
426 case UP_GRAV: *mapptr = SETUP_UP_GRAV; break;
427 case DOWN_GRAV: *mapptr = SETUP_DOWN_GRAV; break;
428 case RIGHT_GRAV: *mapptr = SETUP_RIGHT_GRAV; break;
429 case LEFT_GRAV: *mapptr = SETUP_LEFT_GRAV; break;
430 case ITEM_CONCENTRATOR:
431 *mapptr = SETUP_ITEM_CONCENTRATOR; break;
432
433 case ASTEROID_CONCENTRATOR:
434 *mapptr = SETUP_ASTEROID_CONCENTRATOR; break;
435
436 case DECOR_FILLED: *mapptr = SETUP_DECOR_FILLED; break;
437 case DECOR_RU: *mapptr = SETUP_DECOR_RU; break;
438 case DECOR_RD: *mapptr = SETUP_DECOR_RD; break;
439 case DECOR_LU: *mapptr = SETUP_DECOR_LU; break;
440 case DECOR_LD: *mapptr = SETUP_DECOR_LD; break;
441
442 case WORMHOLE:
443 if (wormhole_i >= Num_wormholes()) {
444 /*
445 * This can happen on an xp2 map if the block mapdata
446 * contains more wormholes than is specified in the
447 * xml data.
448 */
449 warn("Too many wormholes in block mapdata.");
450 *mapptr = SETUP_SPACE;
451 break;
452 }
453 wtype = Wormhole_by_index(wormhole_i)->type;
454 wormhole_i++;
455 switch (wtype) {
456 case WORM_NORMAL:
457 case WORM_FIXED:
458 *mapptr = SETUP_WORM_NORMAL;
459 break;
460 case WORM_IN:
461 *mapptr = SETUP_WORM_IN;
462 break;
463 case WORM_OUT:
464 *mapptr = SETUP_WORM_OUT;
465 break;
466 default:
467 warn("Bad wormhole (%d,%d).", x, y);
468 *mapptr = SETUP_SPACE;
469 break;
470 }
471 break;
472
473 case TREASURE:
474 if (treasure_i >= Num_treasures()) {
475 warn("Too many treasures in block mapdata.");
476 *mapptr = SETUP_SPACE;
477 break;
478 }
479 team = Treasure_by_index(treasure_i)->team;
480 treasure_i++;
481 if (team == TEAM_NOT_SET)
482 team = 0;
483 *mapptr = SETUP_TREASURE + team;
484 break;
485
486 case TARGET:
487 if (target_i >= Num_targets()) {
488 warn("Too many targets in block mapdata.");
489 *mapptr = SETUP_SPACE;
490 break;
491 }
492 team = Target_by_index(target_i)->team;
493 target_i++;
494 if (team == TEAM_NOT_SET)
495 team = 0;
496 *mapptr = SETUP_TARGET + team;
497 break;
498
499 case BASE:
500 if (base_i >= Num_bases()) {
501 warn("Too many bases in block mapdata.");
502 *mapptr = SETUP_SPACE;
503 break;
504 }
505 team = Base_by_index(base_i)->team;
506 if (team == TEAM_NOT_SET)
507 team = 0;
508 dir = Base_by_index(base_i)->dir;
509 base_i++;
510 /* other code should take care of this */
511 assert(dir >= 0);
512 assert(dir < RES);
513 /* round to nearest direction */
514 dir = (((dir + (RES/8)) / (RES/4)) * (RES/4)) % RES;
515 assert(dir == DIR_UP || dir == DIR_RIGHT
516 || dir == DIR_DOWN || dir == DIR_LEFT);
517 switch (dir) {
518 case DIR_UP: *mapptr = SETUP_BASE_UP + team; break;
519 case DIR_RIGHT: *mapptr = SETUP_BASE_RIGHT + team; break;
520 case DIR_DOWN: *mapptr = SETUP_BASE_DOWN + team; break;
521 case DIR_LEFT: *mapptr = SETUP_BASE_LEFT + team; break;
522 default:
523 /* should never happen */
524 warn("Bad base at (%d,%d), (dir = %d).", x, y, dir);
525 *mapptr = SETUP_BASE_UP + team;
526 break;
527 }
528 break;
529
530 case CANNON:
531 if (cannon_i >= Num_cannons()) {
532 warn("Too many cannons in block mapdata.");
533 *mapptr = SETUP_SPACE;
534 break;
535 }
536 dir = Cannon_by_index(cannon_i)->dir;
537 cannon_i++;
538 switch (dir) {
539 case DIR_UP: *mapptr = SETUP_CANNON_UP; break;
540 case DIR_RIGHT: *mapptr = SETUP_CANNON_RIGHT; break;
541 case DIR_DOWN: *mapptr = SETUP_CANNON_DOWN; break;
542 case DIR_LEFT: *mapptr = SETUP_CANNON_LEFT; break;
543 default:
544 warn("Bad cannon at (%d,%d), (dir = %d).", x, y, dir);
545 *mapptr = SETUP_CANNON_UP;
546 break;
547 }
548 break;
549
550 case CHECK:
551 for (i = 0; i < world->NumChecks; i++) {
552 check_t *check = Check_by_index(i);
553 blkpos_t bpos = Clpos_to_blkpos(check->pos);
554
555 if (x != bpos.bx || y != bpos.by)
556 continue;
557 *mapptr = SETUP_CHECK + i;
558 break;
559 }
560 if (i >= world->NumChecks) {
561 warn("Bad checkpoint at (%d,%d).", x, y);
562 *mapptr = SETUP_SPACE;
563 break;
564 }
565 break;
566
567 default:
568 warn("Unknown map type (%d) at (%d,%d).", type, x, y);
569 *mapptr = SETUP_SPACE;
570 break;
571 }
572 }
573 }
574 if (!compress_maps) {
575 type = SETUP_MAP_UNCOMPRESSED;
576 size = numblocks;
577 } else {
578 type = SETUP_MAP_ORDER_XY;
579 size = Compress_map(mapdata, numblocks);
580 if (size <= 0 || size > numblocks) {
581 warn("Map compression error (%d)", size);
582 free(mapdata);
583 return NULL;
584 }
585 if ((mapdata = XREALLOC(unsigned char, mapdata, size)) == NULL) {
586 error("Cannot reallocate mapdata");
587 return NULL;
588 }
589 }
590
591 if (type != SETUP_MAP_UNCOMPRESSED)
592 xpprintf("%s Block map compression ratio is %-4.2f%%\n",
593 showtime(), 100.0 * size / numblocks);
594
595 if ((setup = (setup_t *)malloc(sizeof(setup_t) + size)) == NULL) {
596 error("No memory to hold oldsetup");
597 free(mapdata);
598 return NULL;
599 }
600 memset(setup, 0, sizeof(setup_t) + size);
601 memcpy(setup->map_data, mapdata, size);
602 free(mapdata);
603 setup->setup_size = ((char *) &setup->map_data[0]
604 - (char *) setup) + size;
605 setup->map_data_len = size;
606 setup->map_order = type;
607 setup->lives = world->rules->lives;
608 setup->mode = world->rules->mode;
609 setup->x = world->x;
610 setup->y = world->y;
611 strlcpy(setup->name, world->name, sizeof(setup->name));
612 strlcpy(setup->author, world->author, sizeof(setup->author));
613
614 return setup;
615 }
616
617
618
619
620 /*
621 * Grok block based map data.
622 *
623 * Create world->block using options.mapData.
624 * Free options.mapData.
625 */
Xpmap_grok_map_data(void)626 void Xpmap_grok_map_data(void)
627 {
628 int x = -1, y = world->y - 1, c;
629 char *s = options.mapData;
630 blkpos_t blk;
631
632 if (options.mapData == NULL) {
633 warn("Map didn't have any mapData.");
634 return;
635 }
636
637 while (y >= 0) {
638
639 x++;
640
641 if (options.extraBorder && (x == 0 || x == world->x - 1
642 || y == 0 || y == world->y - 1)) {
643 if (x >= world->x) {
644 x = -1;
645 y--;
646 continue;
647 } else
648 /* make extra border of solid rock */
649 c = XPMAP_FILLED;
650 }
651 else {
652 c = *s;
653 if (c == '\0' || c == EOF) {
654 if (x < world->x) {
655 /* not enough map data on this line */
656 Xpmap_missing_error(world->y - y);
657 c = XPMAP_SPACE;
658 } else
659 c = '\n';
660 } else {
661 if (c == '\n' && x < world->x) {
662 /* not enough map data on this line */
663 Xpmap_missing_error(world->y - y);
664 c = XPMAP_SPACE;
665 } else
666 s++;
667 }
668 }
669 if (x >= world->x || c == '\n') {
670 y--; x = -1;
671 if (c != '\n') { /* Get rest of line */
672 Xpmap_extra_error(world->y - y);
673 while (c != '\n' && c != EOF)
674 c = *s++;
675 }
676 continue;
677 }
678 blk.bx = x;
679 blk.by = y;
680 World_set_block(blk, c);
681 }
682
683 XFREE(options.mapData);
684 }
685
686
687 /*
688 * Determining which team these belong to is done later,
689 * in Find_closest_team().
690 */
Xpmap_place_cannon(blkpos_t blk,int dir)691 static void Xpmap_place_cannon(blkpos_t blk, int dir)
692 {
693 clpos_t pos;
694 int ind;
695
696 switch (dir) {
697 case DIR_UP:
698 pos.cx = (click_t)((blk.bx + 0.5) * BLOCK_CLICKS);
699 pos.cy = (click_t)((blk.by + 0.333) * BLOCK_CLICKS);
700 break;
701 case DIR_LEFT:
702 pos.cx = (click_t)((blk.bx + 0.667) * BLOCK_CLICKS);
703 pos.cy = (click_t)((blk.by + 0.5) * BLOCK_CLICKS);
704 break;
705 case DIR_RIGHT:
706 pos.cx = (click_t)((blk.bx + 0.333) * BLOCK_CLICKS);
707 pos.cy = (click_t)((blk.by + 0.5) * BLOCK_CLICKS);
708 break;
709 case DIR_DOWN:
710 pos.cx = (click_t)((blk.bx + 0.5) * BLOCK_CLICKS);
711 pos.cy = (click_t)((blk.by + 0.667) * BLOCK_CLICKS);
712 break;
713 default:
714 /* can't happen */
715 assert(0 && "Unknown cannon direction.");
716 break;
717 }
718
719 World_set_block(blk, CANNON);
720 ind = World_place_cannon(pos, dir, TEAM_NOT_SET);
721 Cannon_init(Cannon_by_index(ind));
722 }
723
724 /*
725 * The direction of the base should be so that it points
726 * up with respect to the gravity in the region. This
727 * is fixed in Find_base_dir() when the gravity has
728 * been computed.
729 */
Xpmap_place_base(blkpos_t blk,int team)730 static void Xpmap_place_base(blkpos_t blk, int team)
731 {
732 World_set_block(blk, BASE);
733 World_place_base(Block_get_center_clpos(blk), DIR_UP, team, 0);
734 }
735
Xpmap_place_fuel(blkpos_t blk)736 static void Xpmap_place_fuel(blkpos_t blk)
737 {
738 World_set_block(blk, FUEL);
739 World_place_fuel(Block_get_center_clpos(blk), TEAM_NOT_SET);
740 }
741
Xpmap_place_treasure(blkpos_t blk,bool empty)742 static void Xpmap_place_treasure(blkpos_t blk, bool empty)
743 {
744 World_set_block(blk, TREASURE);
745 World_place_treasure(Block_get_center_clpos(blk),
746 TEAM_NOT_SET, empty, 0xff);
747 }
748
Xpmap_place_wormhole(blkpos_t blk,wormtype_t type)749 static void Xpmap_place_wormhole(blkpos_t blk, wormtype_t type)
750 {
751 World_set_block(blk, WORMHOLE);
752 World_place_wormhole(Block_get_center_clpos(blk), type);
753 }
754
Xpmap_place_target(blkpos_t blk)755 static void Xpmap_place_target(blkpos_t blk)
756 {
757 World_set_block(blk, TARGET);
758 World_place_target(Block_get_center_clpos(blk), TEAM_NOT_SET);
759 }
760
Xpmap_place_check(blkpos_t blk,int ind)761 static void Xpmap_place_check(blkpos_t blk, int ind)
762 {
763 if (!BIT(world->rules->mode, TIMING)) {
764 World_set_block(blk, SPACE);
765 return;
766 }
767
768 World_set_block(blk, CHECK);
769 World_place_check(Block_get_center_clpos(blk), ind);
770 }
771
Xpmap_place_item_concentrator(blkpos_t blk)772 static void Xpmap_place_item_concentrator(blkpos_t blk)
773 {
774 World_set_block(blk, ITEM_CONCENTRATOR);
775 World_place_item_concentrator(Block_get_center_clpos(blk));
776 }
777
Xpmap_place_asteroid_concentrator(blkpos_t blk)778 static void Xpmap_place_asteroid_concentrator(blkpos_t blk)
779 {
780 World_set_block(blk, ASTEROID_CONCENTRATOR);
781 World_place_asteroid_concentrator(Block_get_center_clpos(blk));
782 }
783
Xpmap_place_grav(blkpos_t blk,double force,int type)784 static void Xpmap_place_grav(blkpos_t blk,
785 double force, int type)
786 {
787 World_set_block(blk, type);
788 World_place_grav(Block_get_center_clpos(blk), force, type);
789 }
790
Xpmap_place_friction_area(blkpos_t blk)791 static void Xpmap_place_friction_area(blkpos_t blk)
792 {
793 World_set_block(blk, FRICTION);
794 World_place_friction_area(Block_get_center_clpos(blk),
795 options.blockFriction);
796 }
797
Xpmap_place_block(blkpos_t blk,int type)798 static void Xpmap_place_block(blkpos_t blk, int type)
799 {
800 World_set_block(blk, type);
801 }
802
803 /*
804 * Change read tags to internal data, create objects if 'create' is true.
805 */
Xpmap_tags_to_internal_data(void)806 void Xpmap_tags_to_internal_data(void)
807 {
808 int x, y;
809 char c;
810
811 for (x = 0; x < world->x; x++) {
812
813 for (y = 0; y < world->y; y++) {
814
815 blkpos_t blk;
816
817 blk.bx = x;
818 blk.by = y;
819
820 c = world->block[x][y];
821
822 switch (c) {
823 case XPMAP_SPACE:
824 case XPMAP_SPACE_ALT:
825 default:
826 Xpmap_place_block(blk, SPACE);
827 break;
828
829 case XPMAP_FILLED:
830 Xpmap_place_block(blk, FILLED);
831 break;
832 case XPMAP_REC_LU:
833 Xpmap_place_block(blk, REC_LU);
834 break;
835 case XPMAP_REC_RU:
836 Xpmap_place_block(blk, REC_RU);
837 break;
838 case XPMAP_REC_LD:
839 Xpmap_place_block(blk, REC_LD);
840 break;
841 case XPMAP_REC_RD:
842 Xpmap_place_block(blk, REC_RD);
843 break;
844
845 case XPMAP_CANNON_UP:
846 Xpmap_place_cannon(blk, DIR_UP);
847 break;
848 case XPMAP_CANNON_LEFT:
849 Xpmap_place_cannon(blk, DIR_LEFT);
850 break;
851 case XPMAP_CANNON_RIGHT:
852 Xpmap_place_cannon(blk, DIR_RIGHT);
853 break;
854 case XPMAP_CANNON_DOWN:
855 Xpmap_place_cannon(blk, DIR_DOWN);
856 break;
857
858 case XPMAP_FUEL:
859 Xpmap_place_fuel(blk);
860 break;
861 case XPMAP_TREASURE:
862 Xpmap_place_treasure(blk, false);
863 break;
864 case XPMAP_EMPTY_TREASURE:
865 Xpmap_place_treasure(blk, true);
866 break;
867 case XPMAP_TARGET:
868 Xpmap_place_target(blk);
869 break;
870 case XPMAP_ITEM_CONCENTRATOR:
871 Xpmap_place_item_concentrator(blk);
872 break;
873 case XPMAP_ASTEROID_CONCENTRATOR:
874 Xpmap_place_asteroid_concentrator(blk);
875 break;
876 case XPMAP_BASE_ATTRACTOR:
877 Xpmap_place_block(blk, BASE_ATTRACTOR);
878 break;
879 case XPMAP_BASE:
880 Xpmap_place_base(blk, TEAM_NOT_SET);
881 break;
882 case XPMAP_BASE_TEAM_0:
883 case XPMAP_BASE_TEAM_1:
884 case XPMAP_BASE_TEAM_2:
885 case XPMAP_BASE_TEAM_3:
886 case XPMAP_BASE_TEAM_4:
887 case XPMAP_BASE_TEAM_5:
888 case XPMAP_BASE_TEAM_6:
889 case XPMAP_BASE_TEAM_7:
890 case XPMAP_BASE_TEAM_8:
891 case XPMAP_BASE_TEAM_9:
892 Xpmap_place_base(blk, (int) (c - XPMAP_BASE_TEAM_0));
893 break;
894
895 case XPMAP_POS_GRAV:
896 Xpmap_place_grav(blk, -GRAVS_POWER, POS_GRAV);
897 break;
898 case XPMAP_NEG_GRAV:
899 Xpmap_place_grav(blk, GRAVS_POWER, NEG_GRAV);
900 break;
901 case XPMAP_CWISE_GRAV:
902 Xpmap_place_grav(blk, GRAVS_POWER, CWISE_GRAV);
903 break;
904 case XPMAP_ACWISE_GRAV:
905 Xpmap_place_grav(blk, -GRAVS_POWER, ACWISE_GRAV);
906 break;
907 case XPMAP_UP_GRAV:
908 Xpmap_place_grav(blk, GRAVS_POWER, UP_GRAV);
909 break;
910 case XPMAP_DOWN_GRAV:
911 Xpmap_place_grav(blk, -GRAVS_POWER, DOWN_GRAV);
912 break;
913 case XPMAP_RIGHT_GRAV:
914 Xpmap_place_grav(blk, GRAVS_POWER, RIGHT_GRAV);
915 break;
916 case XPMAP_LEFT_GRAV:
917 Xpmap_place_grav(blk, -GRAVS_POWER, LEFT_GRAV);
918 break;
919
920 case XPMAP_WORMHOLE_NORMAL:
921 Xpmap_place_wormhole(blk, WORM_NORMAL);
922 break;
923 case XPMAP_WORMHOLE_IN:
924 Xpmap_place_wormhole(blk, WORM_IN);
925 break;
926 case XPMAP_WORMHOLE_OUT:
927 Xpmap_place_wormhole(blk, WORM_OUT);
928 break;
929
930 case XPMAP_CHECK_0: case XPMAP_CHECK_1:
931 case XPMAP_CHECK_2: case XPMAP_CHECK_3:
932 case XPMAP_CHECK_4: case XPMAP_CHECK_5:
933 case XPMAP_CHECK_6: case XPMAP_CHECK_7:
934 case XPMAP_CHECK_8: case XPMAP_CHECK_9:
935 case XPMAP_CHECK_10: case XPMAP_CHECK_11:
936 case XPMAP_CHECK_12: case XPMAP_CHECK_13:
937 case XPMAP_CHECK_14: case XPMAP_CHECK_15:
938 case XPMAP_CHECK_16: case XPMAP_CHECK_17:
939 case XPMAP_CHECK_18: case XPMAP_CHECK_19:
940 case XPMAP_CHECK_20: case XPMAP_CHECK_21:
941 case XPMAP_CHECK_22: case XPMAP_CHECK_23:
942 case XPMAP_CHECK_24: case XPMAP_CHECK_25:
943 Xpmap_place_check(blk, (int) (c - XPMAP_CHECK_0));
944 break;
945
946 case XPMAP_FRICTION_AREA:
947 Xpmap_place_friction_area(blk);
948 break;
949
950 case XPMAP_DECOR_FILLED:
951 Xpmap_place_block(blk, DECOR_FILLED);
952 break;
953 case XPMAP_DECOR_LU:
954 Xpmap_place_block(blk, DECOR_LU);
955 break;
956 case XPMAP_DECOR_RU:
957 Xpmap_place_block(blk, DECOR_RU);
958 break;
959 case XPMAP_DECOR_LD:
960 Xpmap_place_block(blk, DECOR_LD);
961 break;
962 case XPMAP_DECOR_RD:
963 Xpmap_place_block(blk, DECOR_RD);
964 break;
965 }
966 }
967 }
968 }
969
970
Xpmap_find_map_object_teams(void)971 void Xpmap_find_map_object_teams(void)
972 {
973 int i;
974 clpos_t pos = { 0, 0 };
975
976 if (!BIT(world->rules->mode, TEAM_PLAY))
977 return;
978
979 /* This could return -1 */
980 if (Find_closest_team(pos) == TEAM_NOT_SET) {
981 warn("Broken map: Couldn't find teams for map objects.");
982 return;
983 }
984
985 /*
986 * Determine which team a stuff belongs to.
987 */
988 for (i = 0; i < Num_treasures(); i++) {
989 treasure_t *treasure = Treasure_by_index(i);
990 team_t *teamp;
991
992 treasure->team = Find_closest_team(treasure->pos);
993 teamp = Team_by_index(treasure->team);
994 assert(teamp != NULL);
995
996 teamp->NumTreasures++;
997 if (treasure->empty)
998 teamp->NumEmptyTreasures++;
999 else
1000 teamp->TreasuresLeft++;
1001 }
1002
1003 for (i = 0; i < Num_targets(); i++) {
1004 target_t *targ = Target_by_index(i);
1005
1006 targ->team = Find_closest_team(targ->pos);
1007 }
1008
1009 if (options.teamCannons) {
1010 for (i = 0; i < Num_cannons(); i++) {
1011 cannon_t *cannon = Cannon_by_index(i);
1012
1013 cannon->team = Find_closest_team(cannon->pos);
1014 }
1015 }
1016
1017 for (i = 0; i < Num_fuels(); i++) {
1018 fuel_t *fs = Fuel_by_index(i);
1019
1020 fs->team = Find_closest_team(fs->pos);
1021 }
1022 }
1023
1024
1025 /*
1026 * Find the correct direction of the base, according to the gravity in
1027 * the base region.
1028 *
1029 * If a base attractor is adjacent to a base then the base will point
1030 * to the attractor.
1031 */
Xpmap_find_base_direction(void)1032 void Xpmap_find_base_direction(void)
1033 {
1034 int i;
1035 blkpos_t blk;
1036
1037 for (i = 0; i < Num_bases(); i++) {
1038 base_t *base = Base_by_index(i);
1039 int x, y, dir, att;
1040 vector_t gravity = World_gravity(base->pos);
1041
1042 if (gravity.x == 0.0 && gravity.y == 0.0)
1043 /*
1044 * Undefined direction
1045 * Should be set to direction of gravity!
1046 */
1047 dir = DIR_UP;
1048 else {
1049 double a = findDir(-gravity.x, -gravity.y);
1050
1051 dir = MOD2((int) (a + 0.5), RES);
1052 dir = ((dir + RES/8) / (RES/4)) * (RES/4); /* round it */
1053 dir = MOD2(dir, RES);
1054 }
1055 att = -1;
1056
1057 x = CLICK_TO_BLOCK(base->pos.cx);
1058 y = CLICK_TO_BLOCK(base->pos.cy);
1059
1060 /* First check upwards attractor */
1061 if (y == world->y - 1 && world->block[x][0] == BASE_ATTRACTOR
1062 && BIT(world->rules->mode, WRAP_PLAY)) {
1063 if (att == -1 || dir == DIR_UP)
1064 att = DIR_UP;
1065 }
1066 if (y < world->y - 1 && world->block[x][y + 1] == BASE_ATTRACTOR) {
1067 if (att == -1 || dir == DIR_UP)
1068 att = DIR_UP;
1069 }
1070
1071 /* then downwards */
1072 if (y == 0 && world->block[x][world->y-1] == BASE_ATTRACTOR
1073 && BIT(world->rules->mode, WRAP_PLAY)) {
1074 if (att == -1 || dir == DIR_DOWN)
1075 att = DIR_DOWN;
1076 }
1077 if (y > 0 && world->block[x][y - 1] == BASE_ATTRACTOR) {
1078 if (att == -1 || dir == DIR_DOWN)
1079 att = DIR_DOWN;
1080 }
1081
1082 /* then rightwards */
1083 if (x == world->x - 1 && world->block[0][y] == BASE_ATTRACTOR
1084 && BIT(world->rules->mode, WRAP_PLAY)) {
1085 if (att == -1 || dir == DIR_RIGHT)
1086 att = DIR_RIGHT;
1087 }
1088 if (x < world->x - 1 && world->block[x + 1][y] == BASE_ATTRACTOR) {
1089 if (att == -1 || dir == DIR_RIGHT)
1090 att = DIR_RIGHT;
1091 }
1092
1093 /* then leftwards */
1094 if (x == 0 && world->block[world->x-1][y] == BASE_ATTRACTOR
1095 && BIT(world->rules->mode, WRAP_PLAY)) {
1096 if (att == -1 || dir == DIR_LEFT)
1097 att = DIR_LEFT;
1098 }
1099 if (x > 0 && world->block[x - 1][y] == BASE_ATTRACTOR) {
1100 if (att == -1 || dir == DIR_LEFT)
1101 att = DIR_LEFT;
1102 }
1103
1104 if (att != -1)
1105 dir = att;
1106 base->dir = dir;
1107 }
1108 for (blk.bx = 0; blk.bx < world->x; blk.bx++) {
1109 for (blk.by = 0; blk.by < world->y; blk.by++) {
1110 if (World_get_block(blk) == BASE_ATTRACTOR)
1111 World_set_block(blk, SPACE);
1112 }
1113 }
1114 }
1115
1116
1117 /*
1118 * The following functions is for converting the block based map data
1119 * to polygons.
1120 */
1121
1122 /* number of vertices in polygon */
1123 #define N (2 + 12)
Xpmap_treasure_to_polygon(int treasure_ind)1124 static void Xpmap_treasure_to_polygon(int treasure_ind)
1125 {
1126 int cx, cy, i, r, n;
1127 double angle;
1128 int polystyle, edgestyle;
1129 treasure_t *treasure = Treasure_by_index(treasure_ind);
1130 clpos_t pos[N + 1];
1131
1132 polystyle = P_get_poly_id("treasure_ps");
1133 edgestyle = P_get_edge_id("treasure_es");
1134
1135 cx = treasure->pos.cx - BLOCK_CLICKS / 2;
1136 cy = treasure->pos.cy - BLOCK_CLICKS / 2;
1137
1138 pos[0].cx = cx;
1139 pos[0].cy = cy;
1140 pos[1].cx = cx + (BLOCK_CLICKS - 1);
1141 pos[1].cy = cy;
1142
1143 cx = treasure->pos.cx;
1144 cy = treasure->pos.cy;
1145 /* -1 is to ensure the vertices are inside the block */
1146 r = (BLOCK_CLICKS / 2) - 1;
1147 /* number of points in half circle */
1148 n = N - 2;
1149
1150 for (i = 0; i < n; i++) {
1151 angle = (((double)i)/(n - 1)) * PI;
1152 pos[i + 2].cx = (click_t)(cx + r * cos(angle));
1153 pos[i + 2].cy = (click_t)(cy + r * sin(angle));
1154 }
1155
1156 pos[N] = pos[0];
1157
1158 /* create balltarget */
1159 P_start_balltarget(treasure->team, treasure_ind);
1160 P_start_polygon(pos[0], polystyle);
1161 for (i = 1; i <= N; i++)
1162 P_vertex(pos[i], edgestyle);
1163 P_end_polygon();
1164 P_end_balltarget();
1165
1166 /* create ballarea */
1167 P_start_ballarea();
1168 P_start_polygon(pos[0], polystyle);
1169 for (i = 1; i <= N; i++)
1170 P_vertex(pos[i], edgestyle);
1171 P_end_polygon();
1172 P_end_ballarea();
1173 }
1174 #undef N
1175
1176
Xpmap_block_polygon(clpos_t bpos,int polystyle,int edgestyle,int destroyed_style)1177 static void Xpmap_block_polygon(clpos_t bpos, int polystyle, int edgestyle,
1178 int destroyed_style)
1179 {
1180 clpos_t pos[5];
1181 int i;
1182
1183 bpos.cx = CLICK_TO_BLOCK(bpos.cx) * BLOCK_CLICKS;
1184 bpos.cy = CLICK_TO_BLOCK(bpos.cy) * BLOCK_CLICKS;
1185
1186 pos[0].cx = bpos.cx;
1187 pos[0].cy = bpos.cy;
1188 pos[1].cx = bpos.cx + (BLOCK_CLICKS - 1);
1189 pos[1].cy = bpos.cy;
1190 pos[2].cx = bpos.cx + (BLOCK_CLICKS - 1);
1191 pos[2].cy = bpos.cy + (BLOCK_CLICKS - 1);
1192 pos[3].cx = bpos.cx;
1193 pos[3].cy = bpos.cy + (BLOCK_CLICKS - 1);
1194 pos[4] = pos[0];
1195
1196 P_start_polygon(pos[0], polystyle);
1197 if (destroyed_style >= 0)
1198 P_style("destroyed", destroyed_style);
1199 for (i = 1; i <= 4; i++)
1200 P_vertex(pos[i], edgestyle);
1201 P_end_polygon();
1202 }
1203
1204
Xpmap_target_to_polygon(int target_ind)1205 static void Xpmap_target_to_polygon(int target_ind)
1206 {
1207 int ps, es, ds;
1208 target_t *targ = Target_by_index(target_ind);
1209
1210 ps = P_get_poly_id("target_ps");
1211 es = P_get_edge_id("target_es");
1212 ds = P_get_poly_id("destroyed_ps");
1213
1214 /* create target polygon */
1215 P_start_target(target_ind);
1216 Xpmap_block_polygon(targ->pos, ps, es, ds);
1217 P_end_target();
1218 }
1219
1220
Xpmap_cannon_polygon(cannon_t * cannon,int polystyle,int edgestyle)1221 static void Xpmap_cannon_polygon(cannon_t *cannon,
1222 int polystyle, int edgestyle)
1223 {
1224 clpos_t pos[4], cpos = cannon->pos;
1225 int i, ds;
1226
1227 pos[0] = cannon->pos;
1228
1229 cpos.cx = CLICK_TO_BLOCK(cpos.cx) * BLOCK_CLICKS;
1230 cpos.cy = CLICK_TO_BLOCK(cpos.cy) * BLOCK_CLICKS;
1231
1232 switch (cannon->dir) {
1233 case DIR_RIGHT:
1234 pos[1].cx = cpos.cx;
1235 pos[1].cy = cpos.cy + (BLOCK_CLICKS - 1);
1236 pos[2].cx = cpos.cx;
1237 pos[2].cy = cpos.cy;
1238 break;
1239 case DIR_UP:
1240 pos[1].cx = cpos.cx;
1241 pos[1].cy = cpos.cy;
1242 pos[2].cx = cpos.cx + (BLOCK_CLICKS - 1);
1243 pos[2].cy = cpos.cy;
1244 break;
1245 case DIR_LEFT:
1246 pos[1].cx = cpos.cx + (BLOCK_CLICKS - 1);
1247 pos[1].cy = cpos.cy;
1248 pos[2].cx = cpos.cx + (BLOCK_CLICKS - 1);
1249 pos[2].cy = cpos.cy + (BLOCK_CLICKS - 1);
1250 break;
1251 case DIR_DOWN:
1252 pos[1].cx = cpos.cx + (BLOCK_CLICKS - 1);
1253 pos[1].cy = cpos.cy + (BLOCK_CLICKS - 1);
1254 pos[2].cx = cpos.cx;
1255 pos[2].cy = cpos.cy + (BLOCK_CLICKS - 1);
1256 break;
1257 default:
1258 /* can't happen */
1259 assert(0 && "Unknown cannon direction.");
1260 break;
1261 }
1262 pos[3] = pos[0];
1263
1264 ds = P_get_poly_id("destroyed_ps");
1265 P_start_polygon(pos[0], polystyle);
1266 P_style("destroyed", ds);
1267 for (i = 1; i <= 3; i++)
1268 P_vertex(pos[i], edgestyle);
1269 P_end_polygon();
1270 }
1271
1272
Xpmap_cannon_to_polygon(int cannon_ind)1273 static void Xpmap_cannon_to_polygon(int cannon_ind)
1274 {
1275 int ps, es;
1276 cannon_t *cannon = Cannon_by_index(cannon_ind);
1277
1278 ps = P_get_poly_id("cannon_ps");
1279 es = P_get_edge_id("cannon_es");
1280
1281 P_start_cannon(cannon_ind);
1282 Xpmap_cannon_polygon(cannon, ps, es);
1283 P_end_cannon();
1284 }
1285
1286 #define N 12
Xpmap_wormhole_to_polygon(int wormhole_ind)1287 static void Xpmap_wormhole_to_polygon(int wormhole_ind)
1288 {
1289 int ps, es, i, r;
1290 double angle;
1291 wormhole_t *wormhole = Wormhole_by_index(wormhole_ind);
1292 clpos_t pos[N + 1], wpos;
1293
1294 /* don't make a polygon for an out wormhole */
1295 if (wormhole->type == WORM_OUT)
1296 return;
1297
1298 ps = P_get_poly_id("wormhole_ps");
1299 es = P_get_edge_id("wormhole_es");
1300
1301 wpos = wormhole->pos;
1302 r = WORMHOLE_RADIUS;
1303
1304 for (i = 0; i < N; i++) {
1305 angle = (((double)i)/ N) * 2 * PI;
1306 pos[i].cx = (click_t)(wpos.cx + r * cos(angle));
1307 pos[i].cy = (click_t)(wpos.cy + r * sin(angle));
1308 }
1309 pos[N] = pos[0];
1310
1311 P_start_wormhole(wormhole_ind);
1312 P_start_polygon(pos[0], ps);
1313 for (i = 1; i <= N; i++)
1314 P_vertex(pos[i], es);
1315 P_end_polygon();
1316 P_end_wormhole();
1317 }
1318
Xpmap_friction_area_to_polygon(int fa_ind)1319 static void Xpmap_friction_area_to_polygon(int fa_ind)
1320 {
1321 int ps, es;
1322 friction_area_t *fa = FrictionArea_by_index(fa_ind);
1323
1324 ps = P_get_poly_id("fa_ps");
1325 es = P_get_edge_id("fa_es");
1326
1327 P_start_friction_area(fa_ind);
1328 Xpmap_block_polygon(fa->pos, ps, es, -1);
1329 P_end_friction_area();
1330 }
1331
1332 /*
1333 * Add a wall polygon
1334 *
1335 * The polygon consists of a start block and and endblock and possibly
1336 * some full wall/fuel blocks in between. A total number of numblocks
1337 * blocks are part of the polygon and must be 1 or more. If numblocks
1338 * is one, the startblock and endblock are the same block.
1339 *
1340 * The block coordinates of the first block is (bx, by)
1341 *
1342 * The polygon will have 3 or 4 vertices.
1343 *
1344 * Idea: first assume the polygon is a rectangle, then move
1345 * the vertices depending on the start and end blocks.
1346 *
1347 * The vertex index:
1348 * 0: upper left vertex
1349 * 1: lower left vertex
1350 * 2: lower right vertex
1351 * 3: upper right vertex
1352 * 4: upper left vertex, second time
1353 */
Xpmap_wall_poly(int bx,int by,int startblock,int endblock,int numblocks,int polystyle,int edgestyle)1354 static void Xpmap_wall_poly(int bx, int by,
1355 int startblock, int endblock, int numblocks,
1356 int polystyle, int edgestyle)
1357 {
1358 int i;
1359 clpos_t pos[5]; /* positions of vertices */
1360
1361 if (numblocks < 1)
1362 return;
1363
1364 /* first assume we have a rectangle */
1365 pos[0].cx = bx * BLOCK_CLICKS;
1366 pos[0].cy = (by + 1) * BLOCK_CLICKS - 1;
1367 pos[1].cx = bx * BLOCK_CLICKS;
1368 pos[1].cy = by * BLOCK_CLICKS;
1369 pos[2].cx = (bx + numblocks) * BLOCK_CLICKS - 1;
1370 pos[2].cy = by * BLOCK_CLICKS;
1371 pos[3].cx = (bx + numblocks) * BLOCK_CLICKS - 1;
1372 pos[3].cy = (by + 1) * BLOCK_CLICKS - 1;
1373
1374 /* move the vertices depending on the startblock and endblock */
1375 switch (startblock) {
1376 case FILLED:
1377 case REC_LU:
1378 case REC_LD:
1379 case FUEL:
1380 /* no need to move the leftmost 2 vertices */
1381 break;
1382 case REC_RU:
1383 /* move lower left vertex to the right */
1384 pos[1].cx += (BLOCK_CLICKS - 1);
1385 break;
1386 case REC_RD:
1387 /* move upper left vertex to the right */
1388 pos[0].cx += (BLOCK_CLICKS - 1);
1389 break;
1390 default:
1391 return;
1392 }
1393
1394 switch (endblock) {
1395 case FILLED:
1396 case FUEL:
1397 case REC_RU:
1398 case REC_RD:
1399 /* no need to move the rightmost 2 vertices */
1400 break;
1401 case REC_LU:
1402 pos[2].cx -= (BLOCK_CLICKS - 1);
1403 break;
1404 case REC_LD:
1405 pos[3].cx -= (BLOCK_CLICKS - 1);
1406 break;
1407 default:
1408 return;
1409 }
1410
1411 /*
1412 * Since we want to form a closed loop of line segments, the
1413 * last vertex must equal the first.
1414 */
1415 pos[4] = pos[0];
1416
1417 P_start_polygon(pos[0], polystyle);
1418 for (i = 1; i <= 4; i++)
1419 P_vertex(pos[i], edgestyle);
1420 P_end_polygon();
1421 }
1422
1423
Xpmap_walls_to_polygons(void)1424 static void Xpmap_walls_to_polygons(void)
1425 {
1426 int x, y, x0 = 0;
1427 int numblocks = 0;
1428 int inside = false;
1429 int startblock = 0, endblock = 0, block;
1430 int maxblocks = POLYGON_MAX_OFFSET / BLOCK_CLICKS;
1431 int ps, es;
1432
1433 ps = P_get_poly_id("wall_ps");
1434 es = P_get_edge_id("wall_es");
1435
1436 /*
1437 * x, FILLED = solid wall
1438 * s, REC_LU = wall triangle pointing left and up
1439 * a, REC_RU = wall triangle pointing right and up
1440 * w, REC_LD = wall triangle pointing left and down
1441 * q, REC_RD = wall triangle pointing right and down
1442 * #, FUEL = fuel block
1443 */
1444
1445 for (y = world->y - 1; y >= 0; y--) {
1446 for (x = 0; x < world->x; x++) {
1447 block = world->block[x][y];
1448
1449 if (!inside) {
1450 switch (block) {
1451 case FILLED:
1452 case REC_RU:
1453 case REC_RD:
1454 case FUEL:
1455 x0 = x;
1456 startblock = endblock = block;
1457 inside = true;
1458 numblocks = 1;
1459 break;
1460
1461 case REC_LU:
1462 case REC_LD:
1463 Xpmap_wall_poly(x, y, block, block, 1, ps, es);
1464 break;
1465 default:
1466 break;
1467 }
1468 } else {
1469
1470 switch (block) {
1471 case FILLED:
1472 case FUEL:
1473 numblocks++;
1474 endblock = block;
1475 break;
1476
1477 case REC_RU:
1478 case REC_RD:
1479 /* old polygon ends */
1480 Xpmap_wall_poly(x0, y, startblock, endblock,
1481 numblocks, ps, es);
1482 /* and a new one starts */
1483 x0 = x;
1484 startblock = endblock = block;
1485 numblocks = 1;
1486 break;
1487
1488 case REC_LU:
1489 case REC_LD:
1490 numblocks++;
1491 endblock = block;
1492 Xpmap_wall_poly(x0, y, startblock, endblock,
1493 numblocks, ps, es);
1494 inside = false;
1495 break;
1496
1497 default:
1498 /* none of the above, polygon ends */
1499 Xpmap_wall_poly(x0, y, startblock, endblock,
1500 numblocks, ps, es);
1501 inside = false;
1502 break;
1503 }
1504 }
1505
1506 /*
1507 * We don't want the polygon to have offsets that are too big.
1508 */
1509 if (inside && numblocks == maxblocks) {
1510 Xpmap_wall_poly(x0, y, startblock, endblock,
1511 numblocks, ps, es);
1512 inside = false;
1513 }
1514
1515 }
1516
1517 /* end of row */
1518 if (inside) {
1519 Xpmap_wall_poly(x0, y, startblock, endblock,
1520 numblocks, ps, es);
1521 inside = false;
1522 }
1523 }
1524 }
1525
1526
Xpmap_blocks_to_polygons(void)1527 void Xpmap_blocks_to_polygons(void)
1528 {
1529 int i;
1530
1531 /* create edgestyles and polystyles */
1532 P_edgestyle("wall_es", -1, 0x2244EE, 0);
1533 P_polystyle("wall_ps", 0x0033AA, 0, P_get_edge_id("wall_es"), 0);
1534
1535 P_edgestyle("treasure_es", -1, 0xFF0000, 0);
1536 P_polystyle("treasure_ps", 0xFF0000, 0, P_get_edge_id("treasure_es"), 0);
1537
1538 P_edgestyle("target_es", 3, 0xFF7700, 0);
1539 P_polystyle("target_ps", 0xFF7700, 3, P_get_edge_id("target_es"), 0);
1540
1541 P_edgestyle("cannon_es", 3, 0xFFFFFF, 0);
1542 P_polystyle("cannon_ps", 0xFFFFFF, 2, P_get_edge_id("cannon_es"), 0);
1543
1544 P_edgestyle("destroyed_es", 3, 0xFF0000, 0);
1545 P_polystyle("destroyed_ps", 0xFF0000, 2, P_get_edge_id("destroyed_es"),
1546 STYLE_INVISIBLE|STYLE_INVISIBLE_RADAR);
1547
1548 P_edgestyle("wormhole_es", -1, 0x00FFFF, 0);
1549 P_polystyle("wormhole_ps", 0x00FFFF, 2, P_get_edge_id("wormhole_es"), 0);
1550
1551 P_edgestyle("fa_es", 2, 0xFF1F00, 0);
1552 P_polystyle("fa_ps", 0xCF1F00, 2, P_get_edge_id("fa_es"), 0);
1553
1554 Xpmap_walls_to_polygons();
1555
1556 if (options.polygonMode)
1557 is_polygon_map = true;
1558
1559 for (i = 0; i < Num_treasures(); i++)
1560 Xpmap_treasure_to_polygon(i);
1561
1562 for (i = 0; i < Num_targets(); i++)
1563 Xpmap_target_to_polygon(i);
1564
1565 for (i = 0; i < Num_cannons(); i++)
1566 Xpmap_cannon_to_polygon(i);
1567
1568 for (i = 0; i < Num_wormholes(); i++)
1569 Xpmap_wormhole_to_polygon(i);
1570
1571 for (i = 0; i < Num_frictionAreas(); i++)
1572 Xpmap_friction_area_to_polygon(i);
1573
1574 /*xpprintf("Created %d polygons.\n", num_polys);*/
1575 }
1576